mirror of https://github.com/rusefi/lua.git
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.
This commit is contained in:
parent
e0260eb2d4
commit
1537d6680b
36
lgc.c
36
lgc.c
|
@ -1575,52 +1575,64 @@ static int sweepstep (lua_State *L, global_State *g,
|
||||||
|
|
||||||
static lu_mem singlestep (lua_State *L) {
|
static lu_mem singlestep (lua_State *L) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
|
lu_mem work;
|
||||||
|
lua_assert(!g->gcstopem); /* collector is not reentrant */
|
||||||
|
g->gcstopem = 1; /* no emergency collections while collecting */
|
||||||
switch (g->gcstate) {
|
switch (g->gcstate) {
|
||||||
case GCSpause: {
|
case GCSpause: {
|
||||||
restartcollection(g);
|
restartcollection(g);
|
||||||
g->gcstate = GCSpropagate;
|
g->gcstate = GCSpropagate;
|
||||||
return 1;
|
work = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case GCSpropagate: {
|
case GCSpropagate: {
|
||||||
if (g->gray == NULL) { /* no more gray objects? */
|
if (g->gray == NULL) { /* no more gray objects? */
|
||||||
g->gcstate = GCSenteratomic; /* finish propagate phase */
|
g->gcstate = GCSenteratomic; /* finish propagate phase */
|
||||||
return 0;
|
work = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return propagatemark(g); /* traverse one gray object */
|
work = propagatemark(g); /* traverse one gray object */
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case GCSenteratomic: {
|
case GCSenteratomic: {
|
||||||
lu_mem work = atomic(L); /* work is what was traversed by 'atomic' */
|
work = atomic(L); /* work is what was traversed by 'atomic' */
|
||||||
entersweep(L);
|
entersweep(L);
|
||||||
g->GCestimate = gettotalbytes(g); /* first estimate */;
|
g->GCestimate = gettotalbytes(g); /* first estimate */;
|
||||||
return work;
|
break;
|
||||||
}
|
}
|
||||||
case GCSswpallgc: { /* sweep "regular" objects */
|
case GCSswpallgc: { /* sweep "regular" objects */
|
||||||
return sweepstep(L, g, GCSswpfinobj, &g->finobj);
|
work = sweepstep(L, g, GCSswpfinobj, &g->finobj);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case GCSswpfinobj: { /* sweep objects with finalizers */
|
case GCSswpfinobj: { /* sweep objects with finalizers */
|
||||||
return sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
|
work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case GCSswptobefnz: { /* sweep objects to be finalized */
|
case GCSswptobefnz: { /* sweep objects to be finalized */
|
||||||
return sweepstep(L, g, GCSswpend, NULL);
|
work = sweepstep(L, g, GCSswpend, NULL);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case GCSswpend: { /* finish sweeps */
|
case GCSswpend: { /* finish sweeps */
|
||||||
checkSizes(L, g);
|
checkSizes(L, g);
|
||||||
g->gcstate = GCScallfin;
|
g->gcstate = GCScallfin;
|
||||||
return 0;
|
work = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case GCScallfin: { /* call remaining finalizers */
|
case GCScallfin: { /* call remaining finalizers */
|
||||||
if (g->tobefnz && !g->gcemergency) {
|
if (g->tobefnz && !g->gcemergency) {
|
||||||
int n = runafewfinalizers(L, GCFINMAX);
|
g->gcstopem = 0; /* ok collections during finalizers */
|
||||||
return n * GCFINALIZECOST;
|
work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
|
||||||
}
|
}
|
||||||
else { /* emergency mode or no more finalizers */
|
else { /* emergency mode or no more finalizers */
|
||||||
g->gcstate = GCSpause; /* finish collection */
|
g->gcstate = GCSpause; /* finish collection */
|
||||||
return 0;
|
work = 0;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default: lua_assert(0); return 0;
|
default: lua_assert(0); return 0;
|
||||||
}
|
}
|
||||||
|
g->gcstopem = 0;
|
||||||
|
return work;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
25
lmem.c
25
lmem.c
|
@ -24,12 +24,12 @@
|
||||||
|
|
||||||
#if defined(EMERGENCYGCTESTS)
|
#if defined(EMERGENCYGCTESTS)
|
||||||
/*
|
/*
|
||||||
** First allocation will fail whenever not building initial state
|
** First allocation will fail whenever not building initial state.
|
||||||
** and not shrinking a block. (This fail will trigger 'tryagain' and
|
** (This fail will trigger 'tryagain' and a full GC cycle at every
|
||||||
** a full GC cycle at every allocation.)
|
** allocation.)
|
||||||
*/
|
*/
|
||||||
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
|
||||||
if (completestate(g) && ns > os)
|
if (completestate(g) && ns > 0) /* frees never fail */
|
||||||
return NULL; /* fail */
|
return NULL; /* fail */
|
||||||
else /* normal allocation */
|
else /* normal allocation */
|
||||||
return (*g->frealloc)(g->ud, block, os, ns);
|
return (*g->frealloc)(g->ud, block, os, ns);
|
||||||
|
@ -138,15 +138,17 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) {
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** In case of allocation fail, this function will call the GC to try
|
** In case of allocation fail, this function will do an emergency
|
||||||
** to free some memory and then try the allocation again.
|
** collection to free some memory and then try the allocation again.
|
||||||
** (It should not be called when shrinking a block, because then the
|
** The GC should not be called while state is not fully built, as the
|
||||||
** interpreter may be in the middle of a collection step.)
|
** collector is not yet fully initialized. Also, it should not be called
|
||||||
|
** when 'gcstopem' is true, because then the interpreter is in the
|
||||||
|
** middle of a collection step.
|
||||||
*/
|
*/
|
||||||
static void *tryagain (lua_State *L, void *block,
|
static void *tryagain (lua_State *L, void *block,
|
||||||
size_t osize, size_t nsize) {
|
size_t osize, size_t nsize) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
if (completestate(g)) { /* is state fully build? */
|
if (completestate(g) && !g->gcstopem) {
|
||||||
luaC_fullgc(L, 1); /* try to free some memory... */
|
luaC_fullgc(L, 1); /* try to free some memory... */
|
||||||
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */
|
||||||
}
|
}
|
||||||
|
@ -156,8 +158,6 @@ static void *tryagain (lua_State *L, void *block,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Generic allocation routine.
|
** Generic allocation routine.
|
||||||
** If allocation fails while shrinking a block, do not try again; the
|
|
||||||
** GC shrinks some blocks and it is not reentrant.
|
|
||||||
*/
|
*/
|
||||||
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
||||||
void *newblock;
|
void *newblock;
|
||||||
|
@ -165,8 +165,7 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
|
||||||
lua_assert((osize == 0) == (block == NULL));
|
lua_assert((osize == 0) == (block == NULL));
|
||||||
newblock = firsttry(g, block, osize, nsize);
|
newblock = firsttry(g, block, osize, nsize);
|
||||||
if (l_unlikely(newblock == NULL && nsize > 0)) {
|
if (l_unlikely(newblock == NULL && nsize > 0)) {
|
||||||
if (nsize > osize) /* not shrinking a block? */
|
newblock = tryagain(L, block, osize, nsize);
|
||||||
newblock = tryagain(L, block, osize, nsize);
|
|
||||||
if (newblock == NULL) /* still no memory? */
|
if (newblock == NULL) /* still no memory? */
|
||||||
return NULL; /* do not update 'GCdebt' */
|
return NULL; /* do not update 'GCdebt' */
|
||||||
}
|
}
|
||||||
|
|
1
lstate.c
1
lstate.c
|
@ -379,6 +379,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||||
g->panic = NULL;
|
g->panic = NULL;
|
||||||
g->gcstate = GCSpause;
|
g->gcstate = GCSpause;
|
||||||
g->gckind = KGC_INC;
|
g->gckind = KGC_INC;
|
||||||
|
g->gcstopem = 0;
|
||||||
g->gcemergency = 0;
|
g->gcemergency = 0;
|
||||||
g->finobj = g->tobefnz = g->fixedgc = NULL;
|
g->finobj = g->tobefnz = g->fixedgc = NULL;
|
||||||
g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
|
g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
|
||||||
|
|
1
lstate.h
1
lstate.h
|
@ -260,6 +260,7 @@ typedef struct global_State {
|
||||||
lu_byte currentwhite;
|
lu_byte currentwhite;
|
||||||
lu_byte gcstate; /* state of garbage collector */
|
lu_byte gcstate; /* state of garbage collector */
|
||||||
lu_byte gckind; /* kind of GC running */
|
lu_byte gckind; /* kind of GC running */
|
||||||
|
lu_byte gcstopem; /* stops emergency collections */
|
||||||
lu_byte genminormul; /* control for minor generational collections */
|
lu_byte genminormul; /* control for minor generational collections */
|
||||||
lu_byte genmajormul; /* control for major generational collections */
|
lu_byte genmajormul; /* control for major generational collections */
|
||||||
lu_byte gcrunning; /* true if GC is running */
|
lu_byte gcrunning; /* true if GC is running */
|
||||||
|
|
|
@ -676,6 +676,14 @@ end
|
||||||
-- just to make sure
|
-- just to make sure
|
||||||
assert(collectgarbage'isrunning')
|
assert(collectgarbage'isrunning')
|
||||||
|
|
||||||
|
do -- check that the collector is reentrant in incremental mode
|
||||||
|
setmetatable({}, {__gc = function ()
|
||||||
|
collectgarbage()
|
||||||
|
end})
|
||||||
|
collectgarbage()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
collectgarbage(oldmode)
|
collectgarbage(oldmode)
|
||||||
|
|
||||||
print('OK')
|
print('OK')
|
||||||
|
|
Loading…
Reference in New Issue