aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-07-13 15:43:02 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-07-13 15:43:02 -0300
commit2e297d6ab37c1bb255b6984b91dd92d9080e02c9 (patch)
treed9031b8d7fbd2670efb6f9a7d24a7a08f24de844
parentfb18346dddcb0400d0396111c56a817a8d4bd8bd (diff)
downloadlua-2e297d6ab37c1bb255b6984b91dd92d9080e02c9.tar.gz
lua-2e297d6ab37c1bb255b6984b91dd92d9080e02c9.tar.bz2
lua-2e297d6ab37c1bb255b6984b91dd92d9080e02c9.zip
Fixed bug in generational collection of userdata
During generational collection, a userdatum must become gray and go to a gray list after being traversed (like tables), so that 'correctgraylist' can handle it to its next stage. This commit also added minimum tests for the generational collector, including one that would detect this bug.
-rw-r--r--lgc.c26
-rwxr-xr-xtestes/all.lua4
-rw-r--r--testes/gc.lua4
-rw-r--r--testes/gengc.lua83
4 files changed, 102 insertions, 15 deletions
diff --git a/lgc.c b/lgc.c
index fb02f015..c11a5280 100644
--- a/lgc.c
+++ b/lgc.c
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id$ 2** $Id: lgc.c $
3** Garbage Collector 3** Garbage Collector
4** See Copyright Notice in lua.h 4** See Copyright Notice in lua.h
5*/ 5*/
@@ -212,7 +212,7 @@ void luaC_barrierback_ (lua_State *L, GCObject *o) {
212 lua_assert(g->gckind != KGC_GEN || (isold(o) && getage(o) != G_TOUCHED1)); 212 lua_assert(g->gckind != KGC_GEN || (isold(o) && getage(o) != G_TOUCHED1));
213 if (getage(o) != G_TOUCHED2) /* not already in gray list? */ 213 if (getage(o) != G_TOUCHED2) /* not already in gray list? */
214 linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */ 214 linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */
215 black2gray(o); /* make table gray (again) */ 215 black2gray(o); /* make object gray (again) */
216 setage(o, G_TOUCHED1); /* touched in current cycle */ 216 setage(o, G_TOUCHED1); /* touched in current cycle */
217} 217}
218 218
@@ -515,6 +515,19 @@ static lu_mem traversetable (global_State *g, Table *h) {
515} 515}
516 516
517 517
518static int traverseudata (global_State *g, Udata *u) {
519 int i;
520 markobjectN(g, u->metatable); /* mark its metatable */
521 for (i = 0; i < u->nuvalue; i++)
522 markvalue(g, &u->uv[i].uv);
523 if (g->gckind == KGC_GEN) {
524 linkgclist(u, g->grayagain); /* keep it in some gray list */
525 black2gray(u);
526 }
527 return 1 + u->nuvalue;
528}
529
530
518/* 531/*
519** Check the cache of a prototype, to keep invariants. If the 532** Check the cache of a prototype, to keep invariants. If the
520** cache is white, clear it. (A cache should not prevent the 533** cache is white, clear it. (A cache should not prevent the
@@ -613,15 +626,6 @@ static int traversethread (global_State *g, lua_State *th) {
613} 626}
614 627
615 628
616static int traverseudata (global_State *g, Udata *u) {
617 int i;
618 markobjectN(g, u->metatable); /* mark its metatable */
619 for (i = 0; i < u->nuvalue; i++)
620 markvalue(g, &u->uv[i].uv);
621 return 1 + u->nuvalue;
622}
623
624
625/* 629/*
626** traverse one gray object, turning it to black (except for threads, 630** traverse one gray object, turning it to black (except for threads,
627** which are always gray). 631** which are always gray).
diff --git a/testes/all.lua b/testes/all.lua
index cfe21603..37796254 100755
--- a/testes/all.lua
+++ b/testes/all.lua
@@ -1,5 +1,5 @@
1#!../lua 1#!../lua
2-- $Id: all.lua,v 1.100 2018/03/09 14:23:48 roberto Exp $ 2-- $Id: all.lua $
3-- See Copyright Notice at the end of this file 3-- See Copyright Notice at the end of this file
4 4
5 5
@@ -162,7 +162,7 @@ olddofile('strings.lua')
162olddofile('literals.lua') 162olddofile('literals.lua')
163dofile('tpack.lua') 163dofile('tpack.lua')
164assert(dofile('attrib.lua') == 27) 164assert(dofile('attrib.lua') == 27)
165 165dofile('gengc.lua')
166assert(dofile('locals.lua') == 5) 166assert(dofile('locals.lua') == 5)
167dofile('constructs.lua') 167dofile('constructs.lua')
168dofile('code.lua', true) 168dofile('code.lua', true)
diff --git a/testes/gc.lua b/testes/gc.lua
index 9647cd54..05072efd 100644
--- a/testes/gc.lua
+++ b/testes/gc.lua
@@ -1,7 +1,7 @@
1-- $Id: gc.lua,v 1.82 2018/03/12 14:19:36 roberto Exp $ 1-- $Id: gc.lua $
2-- See Copyright Notice in file all.lua 2-- See Copyright Notice in file all.lua
3 3
4print('testing garbage collection') 4print('testing incremental garbage collection')
5 5
6local debug = require"debug" 6local debug = require"debug"
7 7
diff --git a/testes/gengc.lua b/testes/gengc.lua
new file mode 100644
index 00000000..36aed806
--- /dev/null
+++ b/testes/gengc.lua
@@ -0,0 +1,83 @@
1-- $Id: gengc.lua $
2-- See Copyright Notice in file all.lua
3
4print('testing generational garbage collection')
5
6local debug = require"debug"
7
8assert(collectgarbage("isrunning"))
9
10collectgarbage()
11
12local oldmode = collectgarbage("generational")
13
14
15-- ensure that table barrier evolves correctly
16do
17 local U = {}
18 -- full collection makes 'U' old
19 collectgarbage()
20 assert(not T or T.gcage(U) == "old")
21
22 -- U refers to a new table, so it becomes 'touched1'
23 U[1] = {x = {234}}
24 assert(not T or (T.gcage(U) == "touched1" and T.gcage(U[1]) == "new"))
25
26 -- both U and the table survive one more collection
27 collectgarbage("step", 0)
28 assert(not T or (T.gcage(U) == "touched2" and T.gcage(U[1]) == "survival"))
29
30 -- both U and the table survive yet another collection
31 -- now everything is old
32 collectgarbage("step", 0)
33 assert(not T or (T.gcage(U) == "old" and T.gcage(U[1]) == "old1"))
34
35 -- data was not corrupted
36 assert(U[1].x[1] == 234)
37end
38
39
40if T == nil then
41 (Message or print)('\n >>> testC not active: \z
42 skipping some generational tests <<<\n')
43 print 'OK'
44 return
45end
46
47
48-- ensure that userdata barrier evolves correctly
49do
50 local U = T.newuserdata(0, 1)
51 -- full collection makes 'U' old
52 collectgarbage()
53 assert(T.gcage(U) == "old")
54
55 -- U refers to a new table, so it becomes 'touched1'
56 debug.setuservalue(U, {x = {234}})
57 assert(T.gcage(U) == "touched1" and
58 T.gcage(debug.getuservalue(U)) == "new")
59
60 -- both U and the table survive one more collection
61 collectgarbage("step", 0)
62 assert(T.gcage(U) == "touched2" and
63 T.gcage(debug.getuservalue(U)) == "survival")
64
65 -- both U and the table survive yet another collection
66 -- now everything is old
67 collectgarbage("step", 0)
68 assert(T.gcage(U) == "old" and
69 T.gcage(debug.getuservalue(U)) == "old1")
70
71 -- data was not corrupted
72 assert(debug.getuservalue(U).x[1] == 234)
73end
74
75
76
77-- just to make sure
78assert(collectgarbage'isrunning')
79
80collectgarbage(oldmode)
81
82print('OK')
83