Selaa lähdekoodia

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 vuosi sitten
vanhempi
commit
17e0c29d9b
7 muutettua tiedostoa jossa 92 lisäystä ja 43 poistoa
  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) {
   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 */
   }
 }
 

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

+ 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) {
+  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)++;

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

+ 18 - 8
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.

+ 27 - 10
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)
 
 

+ 4 - 4
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