Bladeren bron

Clear interface between references and predefines

The reference system has a defined way to add initial values to the
table where it operates.
Roberto Ierusalimschy 1 jaar geleden
bovenliggende
commit
17e0c29d9b
7 gewijzigde bestanden met toevoegingen van 92 en 43 verwijderingen
  1. 12 16
      lauxlib.c
  2. 3 0
      lstate.c
  3. 25 3
      ltests.c
  4. 3 2
      lua.h
  5. 18 8
      manual/manual.of
  6. 27 10
      testes/api.lua
  7. 4 4
      testes/coroutine.lua

+ 12 - 16
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) {
 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 */
-  }
-  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 */
   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 - 0
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);

+ 25 - 3
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)++;

+ 3 - 2
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 */

+ 18 - 8
manual/manual.of

@@ -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}
-and by some predefined values.
+by the reference mechanism @seeC{luaL_ref},
+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.
-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}
 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.
-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},
 If @id{ref} is @Lid{LUA_NOREF} or @Lid{LUA_REFNIL},
 @Lid{luaL_unref} does nothing.
 @Lid{luaL_unref} does nothing.

+ 27 - 10
testes/api.lua

@@ -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);
-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)
 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)
-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)
 assert(type(mt) == "thread" and coroutine.running() == mt)
 
 
 
 

+ 4 - 4
testes/coroutine.lua

@@ -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