Przeglądaj źródła

TOUCHED2 objects are not always black

This commit fixes a bug introduced in commit 9cf3299fa. TOUCHED2
objects are always black while the mutator runs, but they can become
temporarily gray inside a minor collection (e.g., if the object is a
weak table).
Roberto Ierusalimschy 5 lat temu
rodzic
commit
f7ce7e5faa
2 zmienionych plików z 19 dodań i 8 usunięć
  1. 2 8
      lgc.c
  2. 17 0
      testes/gengc.lua

+ 2 - 8
lgc.c

@@ -1146,15 +1146,9 @@ static GCObject **correctgraylist (GCObject **p) {
     }
     else {  /* everything else is removed */
       lua_assert(isold(curr));  /* young objects should be white here */
-      if (getage(curr) == G_TOUCHED2) {  /* advance from TOUCHED2... */
+      if (getage(curr) == G_TOUCHED2)  /* advance from TOUCHED2... */
         changeage(curr, G_TOUCHED2, G_OLD);  /* ... to OLD */
-        lua_assert(isblack(curr));  /* TOUCHED2 objects are always black */
-      }
-      else {
-        /* everything else in a gray list should be gray */
-        lua_assert(isgray(curr));
-        gray2black(curr);  /* make object black (to be removed) */
-      }
+      gray2black(curr);  /* make object black (to be removed) */
       goto remove;
     }
     remove: *p = *next; continue;

+ 17 - 0
testes/gengc.lua

@@ -106,6 +106,23 @@ do   -- another bug in 5.4.0
 end
 
 
+do   -- bug introduced in commit 9cf3299fa
+  local t = setmetatable({}, {__mode = "kv"})   -- all-weak table
+  collectgarbage()   -- full collection
+  assert(not T or T.gcage(t) == "old")
+  t[1] = {10}
+  assert(not T or (T.gcage(t) == "touched1" and T.gccolor(t) == "gray"))
+  collectgarbage("step", 0)   -- minor collection
+  assert(not T or (T.gcage(t) == "touched2" and T.gccolor(t) == "black"))
+  collectgarbage("step", 0)   -- minor collection
+  assert(not T or T.gcage(t) == "old")   -- t should be black, but it was gray
+  t[1] = {10}      -- no barrier here, so t was still old
+  collectgarbage("step", 0)   -- minor collection
+  -- t, being old, is ignored by the collection, so it is not cleared
+  assert(t[1] == nil)   -- fails with the bug
+end
+
+
 if T == nil then
   (Message or print)('\n >>> testC not active: \z
                              skipping some generational tests <<<\n')