aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-02-26 11:41:02 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-02-26 11:41:02 -0300
commit1537d6680bb66dc2484e11815bc2cd0e31ca39cc (patch)
treef035f9e28fa2f1a6fba62e1b25d32b8a4b824204
parente0260eb2d4085723302d637dd8f3fca339d18817 (diff)
downloadlua-1537d6680bb66dc2484e11815bc2cd0e31ca39cc.tar.gz
lua-1537d6680bb66dc2484e11815bc2cd0e31ca39cc.tar.bz2
lua-1537d6680bb66dc2484e11815bc2cd0e31ca39cc.zip
New control for reentrancy of emergency collections
Instead of assuming that shrinking a block may be an emergency collection, use an explicit field ('gcstopem') to stop emergency collections while GC is working.
-rw-r--r--lgc.c36
-rw-r--r--lmem.c25
-rw-r--r--lstate.c1
-rw-r--r--lstate.h1
-rw-r--r--testes/gc.lua8
5 files changed, 46 insertions, 25 deletions
diff --git a/lgc.c b/lgc.c
index 94e0486e..b360eed0 100644
--- a/lgc.c
+++ b/lgc.c
@@ -1575,52 +1575,64 @@ static int sweepstep (lua_State *L, global_State *g,
1575 1575
1576static lu_mem singlestep (lua_State *L) { 1576static lu_mem singlestep (lua_State *L) {
1577 global_State *g = G(L); 1577 global_State *g = G(L);
1578 lu_mem work;
1579 lua_assert(!g->gcstopem); /* collector is not reentrant */
1580 g->gcstopem = 1; /* no emergency collections while collecting */
1578 switch (g->gcstate) { 1581 switch (g->gcstate) {
1579 case GCSpause: { 1582 case GCSpause: {
1580 restartcollection(g); 1583 restartcollection(g);
1581 g->gcstate = GCSpropagate; 1584 g->gcstate = GCSpropagate;
1582 return 1; 1585 work = 1;
1586 break;
1583 } 1587 }
1584 case GCSpropagate: { 1588 case GCSpropagate: {
1585 if (g->gray == NULL) { /* no more gray objects? */ 1589 if (g->gray == NULL) { /* no more gray objects? */
1586 g->gcstate = GCSenteratomic; /* finish propagate phase */ 1590 g->gcstate = GCSenteratomic; /* finish propagate phase */
1587 return 0; 1591 work = 0;
1588 } 1592 }
1589 else 1593 else
1590 return propagatemark(g); /* traverse one gray object */ 1594 work = propagatemark(g); /* traverse one gray object */
1595 break;
1591 } 1596 }
1592 case GCSenteratomic: { 1597 case GCSenteratomic: {
1593 lu_mem work = atomic(L); /* work is what was traversed by 'atomic' */ 1598 work = atomic(L); /* work is what was traversed by 'atomic' */
1594 entersweep(L); 1599 entersweep(L);
1595 g->GCestimate = gettotalbytes(g); /* first estimate */; 1600 g->GCestimate = gettotalbytes(g); /* first estimate */;
1596 return work; 1601 break;
1597 } 1602 }
1598 case GCSswpallgc: { /* sweep "regular" objects */ 1603 case GCSswpallgc: { /* sweep "regular" objects */
1599 return sweepstep(L, g, GCSswpfinobj, &g->finobj); 1604 work = sweepstep(L, g, GCSswpfinobj, &g->finobj);
1605 break;
1600 } 1606 }
1601 case GCSswpfinobj: { /* sweep objects with finalizers */ 1607 case GCSswpfinobj: { /* sweep objects with finalizers */
1602 return sweepstep(L, g, GCSswptobefnz, &g->tobefnz); 1608 work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
1609 break;
1603 } 1610 }
1604 case GCSswptobefnz: { /* sweep objects to be finalized */ 1611 case GCSswptobefnz: { /* sweep objects to be finalized */
1605 return sweepstep(L, g, GCSswpend, NULL); 1612 work = sweepstep(L, g, GCSswpend, NULL);
1613 break;
1606 } 1614 }
1607 case GCSswpend: { /* finish sweeps */ 1615 case GCSswpend: { /* finish sweeps */
1608 checkSizes(L, g); 1616 checkSizes(L, g);
1609 g->gcstate = GCScallfin; 1617 g->gcstate = GCScallfin;
1610 return 0; 1618 work = 0;
1619 break;
1611 } 1620 }
1612 case GCScallfin: { /* call remaining finalizers */ 1621 case GCScallfin: { /* call remaining finalizers */
1613 if (g->tobefnz && !g->gcemergency) { 1622 if (g->tobefnz && !g->gcemergency) {
1614 int n = runafewfinalizers(L, GCFINMAX); 1623 g->gcstopem = 0; /* ok collections during finalizers */
1615 return n * GCFINALIZECOST; 1624 work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
1616 } 1625 }
1617 else { /* emergency mode or no more finalizers */ 1626 else { /* emergency mode or no more finalizers */
1618 g->gcstate = GCSpause; /* finish collection */ 1627 g->gcstate = GCSpause; /* finish collection */
1619 return 0; 1628 work = 0;
1620 } 1629 }
1630 break;
1621 } 1631 }
1622 default: lua_assert(0); return 0; 1632 default: lua_assert(0); return 0;
1623 } 1633 }
1634 g->gcstopem = 0;
1635 return work;
1624} 1636}
1625 1637
1626 1638
diff --git a/lmem.c b/lmem.c
index e90f991a..9029d588 100644
--- a/lmem.c
+++ b/lmem.c
@@ -24,12 +24,12 @@
24 24
25#if defined(EMERGENCYGCTESTS) 25#if defined(EMERGENCYGCTESTS)
26/* 26/*
27** First allocation will fail whenever not building initial state 27** First allocation will fail whenever not building initial state.
28** and not shrinking a block. (This fail will trigger 'tryagain' and 28** (This fail will trigger 'tryagain' and a full GC cycle at every
29** a full GC cycle at every allocation.) 29** allocation.)
30*/ 30*/
31static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { 31static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
32 if (completestate(g) && ns > os) 32 if (completestate(g) && ns > 0) /* frees never fail */
33 return NULL; /* fail */ 33 return NULL; /* fail */
34 else /* normal allocation */ 34 else /* normal allocation */
35 return (*g->frealloc)(g->ud, block, os, ns); 35 return (*g->frealloc)(g->ud, block, os, ns);
@@ -138,15 +138,17 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) {
138 138
139 139
140/* 140/*
141** In case of allocation fail, this function will call the GC to try 141** In case of allocation fail, this function will do an emergency
142** to free some memory and then try the allocation again. 142** collection to free some memory and then try the allocation again.
143** (It should not be called when shrinking a block, because then the 143** The GC should not be called while state is not fully built, as the
144** interpreter may be in the middle of a collection step.) 144** collector is not yet fully initialized. Also, it should not be called
145** when 'gcstopem' is true, because then the interpreter is in the
146** middle of a collection step.
145*/ 147*/
146static void *tryagain (lua_State *L, void *block, 148static void *tryagain (lua_State *L, void *block,
147 size_t osize, size_t nsize) { 149 size_t osize, size_t nsize) {
148 global_State *g = G(L); 150 global_State *g = G(L);
149 if (completestate(g)) { /* is state fully build? */ 151 if (completestate(g) && !g->gcstopem) {
150 luaC_fullgc(L, 1); /* try to free some memory... */ 152 luaC_fullgc(L, 1); /* try to free some memory... */
151 return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ 153 return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
152 } 154 }
@@ -156,8 +158,6 @@ static void *tryagain (lua_State *L, void *block,
156 158
157/* 159/*
158** Generic allocation routine. 160** Generic allocation routine.
159** If allocation fails while shrinking a block, do not try again; the
160** GC shrinks some blocks and it is not reentrant.
161*/ 161*/
162void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { 162void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
163 void *newblock; 163 void *newblock;
@@ -165,8 +165,7 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
165 lua_assert((osize == 0) == (block == NULL)); 165 lua_assert((osize == 0) == (block == NULL));
166 newblock = firsttry(g, block, osize, nsize); 166 newblock = firsttry(g, block, osize, nsize);
167 if (l_unlikely(newblock == NULL && nsize > 0)) { 167 if (l_unlikely(newblock == NULL && nsize > 0)) {
168 if (nsize > osize) /* not shrinking a block? */ 168 newblock = tryagain(L, block, osize, nsize);
169 newblock = tryagain(L, block, osize, nsize);
170 if (newblock == NULL) /* still no memory? */ 169 if (newblock == NULL) /* still no memory? */
171 return NULL; /* do not update 'GCdebt' */ 170 return NULL; /* do not update 'GCdebt' */
172 } 171 }
diff --git a/lstate.c b/lstate.c
index 04909db3..c5e3b437 100644
--- a/lstate.c
+++ b/lstate.c
@@ -379,6 +379,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
379 g->panic = NULL; 379 g->panic = NULL;
380 g->gcstate = GCSpause; 380 g->gcstate = GCSpause;
381 g->gckind = KGC_INC; 381 g->gckind = KGC_INC;
382 g->gcstopem = 0;
382 g->gcemergency = 0; 383 g->gcemergency = 0;
383 g->finobj = g->tobefnz = g->fixedgc = NULL; 384 g->finobj = g->tobefnz = g->fixedgc = NULL;
384 g->firstold1 = g->survival = g->old1 = g->reallyold = NULL; 385 g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
diff --git a/lstate.h b/lstate.h
index 0322e2c6..c1283bb6 100644
--- a/lstate.h
+++ b/lstate.h
@@ -260,6 +260,7 @@ typedef struct global_State {
260 lu_byte currentwhite; 260 lu_byte currentwhite;
261 lu_byte gcstate; /* state of garbage collector */ 261 lu_byte gcstate; /* state of garbage collector */
262 lu_byte gckind; /* kind of GC running */ 262 lu_byte gckind; /* kind of GC running */
263 lu_byte gcstopem; /* stops emergency collections */
263 lu_byte genminormul; /* control for minor generational collections */ 264 lu_byte genminormul; /* control for minor generational collections */
264 lu_byte genmajormul; /* control for major generational collections */ 265 lu_byte genmajormul; /* control for major generational collections */
265 lu_byte gcrunning; /* true if GC is running */ 266 lu_byte gcrunning; /* true if GC is running */
diff --git a/testes/gc.lua b/testes/gc.lua
index 80850f92..2332c939 100644
--- a/testes/gc.lua
+++ b/testes/gc.lua
@@ -676,6 +676,14 @@ end
676-- just to make sure 676-- just to make sure
677assert(collectgarbage'isrunning') 677assert(collectgarbage'isrunning')
678 678
679do -- check that the collector is reentrant in incremental mode
680 setmetatable({}, {__gc = function ()
681 collectgarbage()
682 end})
683 collectgarbage()
684end
685
686
679collectgarbage(oldmode) 687collectgarbage(oldmode)
680 688
681print('OK') 689print('OK')