Clear interface between references and predefines
The reference system has a defined way to add initial values to the table where it operates.
This commit is contained in:
28
lauxlib.c
28
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:
|
** The previously freed references form a linked list: t[1] is the index
|
||||||
** t[freelist] is the index of a first free index, or zero if list is
|
** of a first free index, t[t[1]] is the index of the second element,
|
||||||
** empty; t[t[freelist]] is the index of the second element; etc.
|
** etc. A zero signals the end of the list.
|
||||||
*/
|
*/
|
||||||
LUALIB_API int luaL_ref (lua_State *L, int t) {
|
LUALIB_API int luaL_ref (lua_State *L, int t) {
|
||||||
int ref;
|
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 */
|
return LUA_REFNIL; /* 'nil' has a unique fixed reference */
|
||||||
}
|
}
|
||||||
t = lua_absindex(L, t);
|
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 */
|
ref = 0; /* list is empty */
|
||||||
lua_pushinteger(L, 0); /* initialize as an empty list */
|
lua_pushinteger(L, 0); /* initialize as an empty list */
|
||||||
lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */
|
lua_rawseti(L, t, 1); /* ref = t[1] = 0 */
|
||||||
}
|
|
||||||
else { /* already initialized */
|
|
||||||
lua_assert(lua_isinteger(L, -1));
|
|
||||||
ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
|
|
||||||
}
|
}
|
||||||
lua_pop(L, 1); /* remove element from stack */
|
lua_pop(L, 1); /* remove element from stack */
|
||||||
if (ref != 0) { /* any free element? */
|
if (ref != 0) { /* any free element? */
|
||||||
lua_rawgeti(L, t, ref); /* remove it from list */
|
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 */
|
else /* no free elements */
|
||||||
ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */
|
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) {
|
LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
|
||||||
if (ref >= 0) {
|
if (ref >= 0) {
|
||||||
t = lua_absindex(L, t);
|
t = lua_absindex(L, t);
|
||||||
lua_rawgeti(L, t, freelist);
|
lua_rawgeti(L, t, 1);
|
||||||
lua_assert(lua_isinteger(L, -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_pushinteger(L, ref);
|
||||||
lua_rawseti(L, t, freelist); /* t[freelist] = ref */
|
lua_rawseti(L, t, 1); /* t[1] = ref */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
lstate.c
3
lstate.c
@@ -189,6 +189,9 @@ static void init_registry (lua_State *L, global_State *g) {
|
|||||||
Table *registry = luaH_new(L);
|
Table *registry = luaH_new(L);
|
||||||
sethvalue(L, &g->l_registry, registry);
|
sethvalue(L, &g->l_registry, registry);
|
||||||
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
|
luaH_resize(L, registry, LUA_RIDX_LAST, 0);
|
||||||
|
/* registry[1] = false */
|
||||||
|
setbfvalue(&aux);
|
||||||
|
luaH_setint(L, registry, 1, &aux);
|
||||||
/* registry[LUA_RIDX_MAINTHREAD] = L */
|
/* registry[LUA_RIDX_MAINTHREAD] = L */
|
||||||
setthvalue(L, &aux, L);
|
setthvalue(L, &aux, L);
|
||||||
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &aux);
|
luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &aux);
|
||||||
|
|||||||
28
ltests.c
28
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) {
|
static int tref (lua_State *L) {
|
||||||
|
int t = getreftable(L);
|
||||||
int level = lua_gettop(L);
|
int level = lua_gettop(L);
|
||||||
luaL_checkany(L, 1);
|
luaL_checkany(L, 1);
|
||||||
lua_pushvalue(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 */
|
cast_void(level); /* to avoid warnings */
|
||||||
lua_assert(lua_gettop(L) == level+1); /* +1 for result */
|
lua_assert(lua_gettop(L) == level+1); /* +1 for result */
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int getref (lua_State *L) {
|
static int getref (lua_State *L) {
|
||||||
|
int t = getreftable(L);
|
||||||
int level = lua_gettop(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 */
|
cast_void(level); /* to avoid warnings */
|
||||||
lua_assert(lua_gettop(L) == level+1);
|
lua_assert(lua_gettop(L) == level+1);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unref (lua_State *L) {
|
static int unref (lua_State *L) {
|
||||||
|
int t = getreftable(L);
|
||||||
int level = lua_gettop(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 */
|
cast_void(level); /* to avoid warnings */
|
||||||
lua_assert(lua_gettop(L) == level);
|
lua_assert(lua_gettop(L) == level);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1373,6 +1385,16 @@ static int getnum_aux (lua_State *L, lua_State *L1, const char **pc) {
|
|||||||
(*pc)++;
|
(*pc)++;
|
||||||
return res;
|
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 == '-') {
|
else if (**pc == '-') {
|
||||||
sig = -1;
|
sig = -1;
|
||||||
(*pc)++;
|
(*pc)++;
|
||||||
|
|||||||
5
lua.h
5
lua.h
@@ -80,9 +80,10 @@ typedef struct lua_State lua_State;
|
|||||||
|
|
||||||
|
|
||||||
/* predefined values in the registry */
|
/* 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_GLOBALS 2
|
||||||
#define LUA_RIDX_LAST LUA_RIDX_GLOBALS
|
#define LUA_RIDX_MAINTHREAD 3
|
||||||
|
#define LUA_RIDX_LAST 3
|
||||||
|
|
||||||
|
|
||||||
/* type of numbers in Lua */
|
/* type of numbers in Lua */
|
||||||
|
|||||||
@@ -2645,8 +2645,8 @@ string keys starting with an underscore followed by
|
|||||||
uppercase letters are reserved for Lua.
|
uppercase letters are reserved for Lua.
|
||||||
|
|
||||||
The integer keys in the registry are used
|
The integer keys in the registry are used
|
||||||
by the reference mechanism @seeC{luaL_ref}
|
by the reference mechanism @seeC{luaL_ref},
|
||||||
and by some predefined values.
|
with some predefined values.
|
||||||
Therefore, integer keys in the registry
|
Therefore, integer keys in the registry
|
||||||
must not be used for other purposes.
|
must not be used for other purposes.
|
||||||
|
|
||||||
@@ -6018,11 +6018,21 @@ Creates and returns a @def{reference},
|
|||||||
in the table at index @id{t},
|
in the table at index @id{t},
|
||||||
for the object on the top of the stack (and pops the object).
|
for the object on the top of the stack (and pops the object).
|
||||||
|
|
||||||
A reference is a unique integer key.
|
The reference system uses the integer keys of the table.
|
||||||
As long as you do not manually add integer keys into the table @id{t},
|
A reference is a unique integer key;
|
||||||
@Lid{luaL_ref} ensures the uniqueness of the key it returns.
|
@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}
|
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.
|
The function @Lid{luaL_unref} frees a reference.
|
||||||
|
|
||||||
If the object on the top of the stack is @nil,
|
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}
|
Releases the reference @id{ref} from the table at index @id{t}
|
||||||
@seeC{luaL_ref}.
|
@seeC{luaL_ref}.
|
||||||
The entry is removed from the table,
|
The entry is removed from the table,
|
||||||
so that the referred object can be collected.
|
so that the referred object can be collected and
|
||||||
The reference @id{ref} is also freed to be used again.
|
the reference @id{ref} can be used again by @Lid{luaL_ref}.
|
||||||
|
|
||||||
If @id{ref} is @Lid{LUA_NOREF} or @Lid{LUA_REFNIL},
|
If @id{ref} is @Lid{LUA_NOREF} or @Lid{LUA_REFNIL},
|
||||||
@Lid{luaL_unref} does nothing.
|
@Lid{luaL_unref} does nothing.
|
||||||
|
|||||||
@@ -467,7 +467,7 @@ for i = 1,lim do
|
|||||||
prog[#prog + 1] = "pushnum " .. i * 10
|
prog[#prog + 1] = "pushnum " .. i * 10
|
||||||
end
|
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)
|
prog[#prog + 1] = "insert " .. -(2*lim + 2)
|
||||||
|
|
||||||
for i = 1,lim do
|
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)
|
assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil)
|
||||||
|
|
||||||
local d = T.ref(a);
|
-- Test references in an arbitrary table
|
||||||
local e = T.ref(b);
|
local reftable = {}
|
||||||
local f = T.ref(c);
|
local d = T.ref(a, reftable);
|
||||||
t = {T.getref(d), T.getref(e), T.getref(f)}
|
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)
|
assert(t[1] == a and t[2] == b and t[3] == c)
|
||||||
|
|
||||||
t=nil; a=nil; c=nil;
|
t=nil; a=nil; c=nil;
|
||||||
T.unref(e); T.unref(f)
|
T.unref(e, reftable); T.unref(f, reftable)
|
||||||
|
|
||||||
collectgarbage()
|
collectgarbage()
|
||||||
|
|
||||||
-- check that unref objects have been collected
|
-- check that unref objects have been collected
|
||||||
assert(#cl == 1 and cl[1] == nc)
|
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)
|
assert(type(x) == 'userdata' and debug.getmetatable(x) == tt)
|
||||||
x =nil
|
x =nil
|
||||||
tt.b = b -- create cycle
|
tt.b = b -- create cycle
|
||||||
tt=nil -- frees tt for GC
|
tt=nil -- frees tt for GC
|
||||||
A = nil
|
A = nil
|
||||||
b = nil
|
b = nil
|
||||||
T.unref(d);
|
T.unref(d, reftable);
|
||||||
local n5 = T.newuserdata(0)
|
local n5 = T.newuserdata(0)
|
||||||
debug.setmetatable(n5, {__gc=F})
|
debug.setmetatable(n5, {__gc=F})
|
||||||
n5 = T.udataval(n5)
|
n5 = T.udataval(n5)
|
||||||
@@ -960,6 +962,21 @@ assert(#cl == 4)
|
|||||||
-- check order of collection
|
-- check order of collection
|
||||||
assert(cl[2] == n5 and cl[3] == nb and cl[4] == na)
|
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"
|
collectgarbage"restart"
|
||||||
|
|
||||||
|
|
||||||
@@ -1363,8 +1380,8 @@ end)
|
|||||||
|
|
||||||
-- testing threads
|
-- testing threads
|
||||||
|
|
||||||
-- get main thread from registry (at index LUA_RIDX_MAINTHREAD == 1)
|
-- get main thread from registry
|
||||||
local mt = T.testC("rawgeti R 1; return 1")
|
local mt = T.testC("rawgeti R !M; return 1")
|
||||||
assert(type(mt) == "thread" and coroutine.running() == mt)
|
assert(type(mt) == "thread" and coroutine.running() == mt)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -681,7 +681,7 @@ else
|
|||||||
c == "ERRRUN" and d == 4)
|
c == "ERRRUN" and d == 4)
|
||||||
|
|
||||||
a, b, c, d = T.testC([[
|
a, b, c, d = T.testC([[
|
||||||
rawgeti R 1 # get main thread
|
rawgeti R !M # get main thread
|
||||||
pushnum 10;
|
pushnum 10;
|
||||||
pushnum 20;
|
pushnum 20;
|
||||||
resume -3 2;
|
resume -3 2;
|
||||||
@@ -699,7 +699,7 @@ else
|
|||||||
assert(T.testC(state, "newthread; isyieldable -1; remove 1; return 1"))
|
assert(T.testC(state, "newthread; isyieldable -1; remove 1; return 1"))
|
||||||
|
|
||||||
-- main thread is not yieldable
|
-- 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")
|
T.testC(state, "settop 0")
|
||||||
|
|
||||||
@@ -711,7 +711,7 @@ else
|
|||||||
return 'ok']]))
|
return 'ok']]))
|
||||||
|
|
||||||
local t = table.pack(T.testC(state, [[
|
local t = table.pack(T.testC(state, [[
|
||||||
rawgeti R 1 # get main thread
|
rawgeti R !M # get main thread
|
||||||
pushstring 'XX'
|
pushstring 'XX'
|
||||||
getglobal X # get function for body
|
getglobal X # get function for body
|
||||||
pushstring AA # arg
|
pushstring AA # arg
|
||||||
@@ -720,7 +720,7 @@ else
|
|||||||
setglobal T # top
|
setglobal T # top
|
||||||
setglobal B # second yielded value
|
setglobal B # second yielded value
|
||||||
setglobal A # fist yielded value
|
setglobal A # fist yielded value
|
||||||
rawgeti R 1 # get main thread
|
rawgeti R !M # get main thread
|
||||||
pushnum 5 # arg (noise)
|
pushnum 5 # arg (noise)
|
||||||
resume 1 1 # after coroutine ends, previous stack is back
|
resume 1 1 # after coroutine ends, previous stack is back
|
||||||
pushstatus
|
pushstatus
|
||||||
|
|||||||
Reference in New Issue
Block a user