diff options
| -rw-r--r-- | lgc.c | 36 | ||||
| -rw-r--r-- | lmem.c | 25 | ||||
| -rw-r--r-- | lstate.c | 1 | ||||
| -rw-r--r-- | lstate.h | 1 | ||||
| -rw-r--r-- | testes/gc.lua | 8 |
5 files changed, 46 insertions, 25 deletions
| @@ -1575,52 +1575,64 @@ static int sweepstep (lua_State *L, global_State *g, | |||
| 1575 | 1575 | ||
| 1576 | static lu_mem singlestep (lua_State *L) { | 1576 | static 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 | ||
| @@ -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 | */ |
| 31 | static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { | 31 | static 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 | */ |
| 146 | static void *tryagain (lua_State *L, void *block, | 148 | static 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 | */ |
| 162 | void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { | 162 | void *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 | } |
| @@ -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; |
| @@ -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 |
| 677 | assert(collectgarbage'isrunning') | 677 | assert(collectgarbage'isrunning') |
| 678 | 678 | ||
| 679 | do -- check that the collector is reentrant in incremental mode | ||
| 680 | setmetatable({}, {__gc = function () | ||
| 681 | collectgarbage() | ||
| 682 | end}) | ||
| 683 | collectgarbage() | ||
| 684 | end | ||
| 685 | |||
| 686 | |||
| 679 | collectgarbage(oldmode) | 687 | collectgarbage(oldmode) |
| 680 | 688 | ||
| 681 | print('OK') | 689 | print('OK') |
