mirror of
https://github.com/lua/lua
synced 2024-12-27 12:49:43 +03:00
Option 0 for step multiplier makes GC non-incremental
This commit is contained in:
parent
4eda1acafa
commit
666e95a66d
67
lgc.c
67
lgc.c
@ -93,6 +93,7 @@
|
||||
*/
|
||||
#define markobjectN(g,t) { if (t) markobject(g,t); }
|
||||
|
||||
|
||||
static void reallymarkobject (global_State *g, GCObject *o);
|
||||
static l_obj atomic (lua_State *L);
|
||||
static void entersweep (lua_State *L);
|
||||
@ -831,10 +832,10 @@ static void freeobj (lua_State *L, GCObject *o) {
|
||||
** for next collection cycle. Return where to continue the traversal or
|
||||
** NULL if list is finished.
|
||||
*/
|
||||
static GCObject **sweeplist (lua_State *L, GCObject **p, int countin) {
|
||||
static GCObject **sweeplist (lua_State *L, GCObject **p, l_obj countin) {
|
||||
global_State *g = G(L);
|
||||
int ow = otherwhite(g);
|
||||
int i;
|
||||
l_obj i;
|
||||
int white = luaC_white(g); /* current white */
|
||||
for (i = 0; *p != NULL && i < countin; i++) {
|
||||
GCObject *curr = *p;
|
||||
@ -1357,8 +1358,8 @@ static void setminordebt (global_State *g) {
|
||||
** collection.
|
||||
*/
|
||||
static void entergen (lua_State *L, global_State *g) {
|
||||
luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */
|
||||
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
|
||||
luaC_runtilstate(L, GCSpause, 1); /* prepare to start a new cycle */
|
||||
luaC_runtilstate(L, GCSpropagate, 1); /* start new cycle */
|
||||
atomic(L); /* propagates all and then do the atomic stuff */
|
||||
atomic2gen(L, g);
|
||||
setminordebt(g); /* set debt assuming next cycle will be minor */
|
||||
@ -1515,10 +1516,14 @@ static l_obj atomic (lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Do a sweep step. The normal case (not fast) sweeps at most GCSWEEPMAX
|
||||
** elements. The fast case sweeps the whole list.
|
||||
*/
|
||||
static void sweepstep (lua_State *L, global_State *g,
|
||||
int nextstate, GCObject **nextlist) {
|
||||
int nextstate, GCObject **nextlist, int fast) {
|
||||
if (g->sweepgc)
|
||||
g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
|
||||
g->sweepgc = sweeplist(L, g->sweepgc, fast ? MAX_LOBJ : GCSWEEPMAX);
|
||||
else { /* enter next state */
|
||||
g->gcstate = nextstate;
|
||||
g->sweepgc = nextlist;
|
||||
@ -1526,7 +1531,19 @@ static void sweepstep (lua_State *L, global_State *g,
|
||||
}
|
||||
|
||||
|
||||
static l_obj singlestep (lua_State *L) {
|
||||
/*
|
||||
** Performs one incremental "step" in an incremental garbage collection.
|
||||
** For indivisible work, a step goes to the next state. When marking
|
||||
** (propagating), a step traverses one object. When sweeping, a step
|
||||
** sweeps GCSWEEPMAX objects, to avoid a big overhead for sweeping
|
||||
** objects one by one. (Sweeping is inexpensive, no matter the
|
||||
** object.) When 'fast' is true, 'singlestep' tries to finish a state
|
||||
** "as fast as possible". In particular, it skips the propagation
|
||||
** phase and leaves all objects to be traversed by the atomic phase:
|
||||
** That avoids traversing twice some objects, such as theads and
|
||||
** weak tables.
|
||||
*/
|
||||
static l_obj singlestep (lua_State *L, int fast) {
|
||||
global_State *g = G(L);
|
||||
l_obj work;
|
||||
lua_assert(!g->gcstopem); /* collector is not reentrant */
|
||||
@ -1539,7 +1556,7 @@ static l_obj singlestep (lua_State *L) {
|
||||
break;
|
||||
}
|
||||
case GCSpropagate: {
|
||||
if (g->gray == NULL) { /* no more gray objects? */
|
||||
if (fast || g->gray == NULL) {
|
||||
g->gcstate = GCSenteratomic; /* finish propagate phase */
|
||||
work = 0;
|
||||
}
|
||||
@ -1556,17 +1573,17 @@ static l_obj singlestep (lua_State *L) {
|
||||
break;
|
||||
}
|
||||
case GCSswpallgc: { /* sweep "regular" objects */
|
||||
sweepstep(L, g, GCSswpfinobj, &g->finobj);
|
||||
sweepstep(L, g, GCSswpfinobj, &g->finobj, fast);
|
||||
work = GCSWEEPMAX;
|
||||
break;
|
||||
}
|
||||
case GCSswpfinobj: { /* sweep objects with finalizers */
|
||||
sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
|
||||
sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast);
|
||||
work = GCSWEEPMAX;
|
||||
break;
|
||||
}
|
||||
case GCSswptobefnz: { /* sweep objects to be finalized */
|
||||
sweepstep(L, g, GCSswpend, NULL);
|
||||
sweepstep(L, g, GCSswpend, NULL, fast);
|
||||
work = GCSWEEPMAX;
|
||||
break;
|
||||
}
|
||||
@ -1596,14 +1613,15 @@ static l_obj singlestep (lua_State *L) {
|
||||
|
||||
|
||||
/*
|
||||
** advances the garbage collector until it reaches a state allowed
|
||||
** by 'statemask'
|
||||
** Advances the garbage collector until it reaches the given state.
|
||||
** (The option 'fast' is only for testing; in normal code, 'fast'
|
||||
** here is always true.)
|
||||
*/
|
||||
void luaC_runtilstate (lua_State *L, int statesmask) {
|
||||
void luaC_runtilstate (lua_State *L, int state, int fast) {
|
||||
global_State *g = G(L);
|
||||
lua_assert(g->gckind == KGC_INC);
|
||||
while (!testbit(statesmask, g->gcstate))
|
||||
singlestep(L);
|
||||
while (state != g->gcstate)
|
||||
singlestep(L, fast);
|
||||
}
|
||||
|
||||
|
||||
@ -1618,8 +1636,13 @@ void luaC_runtilstate (lua_State *L, int statesmask) {
|
||||
static void incstep (lua_State *L, global_State *g) {
|
||||
l_obj stepsize = cast(l_obj, 1) << g->gcstepsize;
|
||||
l_obj work2do = applygcparam(g, gcstepmul, stepsize);
|
||||
do { /* repeat until pause or enough "credit" (negative debt) */
|
||||
l_obj work = singlestep(L); /* perform one single step */
|
||||
int fast = 0;
|
||||
if (work2do == 0) { /* special case: do a full collection */
|
||||
work2do = MAX_LOBJ; /* do unlimited work */
|
||||
fast = 1;
|
||||
}
|
||||
do { /* repeat until pause or enough work */
|
||||
l_obj work = singlestep(L, fast); /* perform one single step */
|
||||
if (g->gckind == KGC_GENMINOR) /* returned to minor collections? */
|
||||
return; /* nothing else to be done here */
|
||||
work2do -= work;
|
||||
@ -1665,13 +1688,11 @@ static void fullinc (lua_State *L, global_State *g) {
|
||||
if (keepinvariant(g)) /* black objects? */
|
||||
entersweep(L); /* sweep everything to turn them back to white */
|
||||
/* finish any pending sweep phase to start a new cycle */
|
||||
luaC_runtilstate(L, bitmask(GCSpause));
|
||||
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
|
||||
g->gcstate = GCSenteratomic; /* go straight to atomic phase ??? */
|
||||
luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */
|
||||
luaC_runtilstate(L, GCSpause, 1);
|
||||
luaC_runtilstate(L, GCScallfin, 1); /* run up to finalizers */
|
||||
/* 'marked' must be correct after a full GC cycle */
|
||||
lua_assert(g->marked == gettotalobjs(g));
|
||||
luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
|
||||
luaC_runtilstate(L, GCSpause, 1); /* finish collection */
|
||||
setpause(g);
|
||||
}
|
||||
|
||||
|
12
lgc.h
12
lgc.h
@ -182,12 +182,14 @@
|
||||
|
||||
/* incremental */
|
||||
|
||||
/* wait memory to double before starting new cycle */
|
||||
#define LUAI_GCPAUSE 200
|
||||
/* Number of objects must be LUAI_GCPAUSE% before starting new cycle */
|
||||
#define LUAI_GCPAUSE 300
|
||||
|
||||
#define LUAI_GCMUL 300 /* step multiplier */
|
||||
/* Step multiplier. (Roughly, the collector handles LUAI_GCMUL% objects
|
||||
for each new allocated object.) */
|
||||
#define LUAI_GCMUL 200
|
||||
|
||||
/* how many objects to allocate before next GC step (log2) */
|
||||
/* How many objects to allocate before next GC step (log2) */
|
||||
#define LUAI_GCSTEPSIZE 8 /* 256 objects */
|
||||
|
||||
|
||||
@ -244,7 +246,7 @@
|
||||
LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
|
||||
LUAI_FUNC void luaC_freeallobjects (lua_State *L);
|
||||
LUAI_FUNC void luaC_step (lua_State *L);
|
||||
LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
|
||||
LUAI_FUNC void luaC_runtilstate (lua_State *L, int state, int fast);
|
||||
LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
|
||||
LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
|
||||
LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz,
|
||||
|
@ -33,7 +33,8 @@ typedef unsigned long lu_mem;
|
||||
typedef long l_obj;
|
||||
#endif /* } */
|
||||
|
||||
#define MAX_LOBJ cast(l_obj, ~cast(lu_mem, 0) >> 1)
|
||||
#define MAX_LOBJ \
|
||||
cast(l_obj, (cast(lu_mem, 1) << (sizeof(l_obj) * CHAR_BIT - 1)) - 1)
|
||||
|
||||
|
||||
/* chars used as small naturals (so that 'char' is reserved for characters) */
|
||||
@ -44,7 +45,10 @@ typedef signed char ls_byte;
|
||||
/* maximum value for size_t */
|
||||
#define MAX_SIZET ((size_t)(~(size_t)0))
|
||||
|
||||
/* maximum size visible for Lua (must be representable in a lua_Integer) */
|
||||
/*
|
||||
** Maximum size for strings and userdata visible for Lua (should be
|
||||
** representable in a lua_Integer)
|
||||
*/
|
||||
#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
|
||||
: (size_t)(LUA_MAXINTEGER))
|
||||
|
||||
|
8
ltests.c
8
ltests.c
@ -941,10 +941,10 @@ static int gc_printobj (lua_State *L) {
|
||||
|
||||
static int gc_state (lua_State *L) {
|
||||
static const char *statenames[] = {
|
||||
"propagate", "atomic", "enteratomic", "sweepallgc", "sweepfinobj",
|
||||
"propagate", "atomic", "sweepallgc", "sweepfinobj",
|
||||
"sweeptobefnz", "sweepend", "callfin", "pause", ""};
|
||||
static const int states[] = {
|
||||
GCSpropagate, GCSenteratomic, GCSatomic, GCSswpallgc, GCSswpfinobj,
|
||||
GCSpropagate, GCSenteratomic, GCSswpallgc, GCSswpfinobj,
|
||||
GCSswptobefnz, GCSswpend, GCScallfin, GCSpause, -1};
|
||||
int option = states[luaL_checkoption(L, 1, "", statenames)];
|
||||
if (option == -1) {
|
||||
@ -957,9 +957,9 @@ static int gc_state (lua_State *L) {
|
||||
luaL_error(L, "cannot change states in generational mode");
|
||||
lua_lock(L);
|
||||
if (option < g->gcstate) { /* must cross 'pause'? */
|
||||
luaC_runtilstate(L, bitmask(GCSpause)); /* run until pause */
|
||||
luaC_runtilstate(L, GCSpause, 1); /* run until pause */
|
||||
}
|
||||
luaC_runtilstate(L, bitmask(option));
|
||||
luaC_runtilstate(L, option, 0); /* do not skip propagation state */
|
||||
lua_assert(G(L)->gcstate == option);
|
||||
lua_unlock(L);
|
||||
return 0;
|
||||
|
@ -664,7 +664,7 @@ Values equal to or less than 100 mean the collector will not wait to
|
||||
start a new cycle.
|
||||
A value of 200 means that the collector waits for
|
||||
the total number of objects to double before starting a new cycle.
|
||||
The default value is 200; the maximum value is 1000.
|
||||
The default value is 300; the maximum value is 1000.
|
||||
|
||||
The garbage-collector step multiplier
|
||||
controls the speed of the collector relative to
|
||||
@ -674,7 +674,9 @@ how many objects it marks or sweeps for each object created.
|
||||
Larger values make the collector more aggressive.
|
||||
Beware that values too small can
|
||||
make the collector too slow to ever finish a cycle.
|
||||
The default value is 300; the maximum value is 1000.
|
||||
The default value is 200; the maximum value is 1000.
|
||||
As a special case, a zero value means unlimited work,
|
||||
effectively producing a non-incremental, stop-the-world collector.
|
||||
|
||||
The garbage-collector step size controls the
|
||||
size of each incremental step,
|
||||
@ -682,9 +684,7 @@ specifically how many objects the interpreter creates
|
||||
before performing a step.
|
||||
This parameter is logarithmic:
|
||||
A value of @M{n} means the interpreter will create @M{2@sp{n}}
|
||||
objects between steps and perform equivalent work during the step.
|
||||
A large value (e.g., 60) makes the collector a stop-the-world
|
||||
(non-incremental) collector.
|
||||
objects between steps.
|
||||
The default value is 8,
|
||||
which means steps of approximately @N{256 objects}.
|
||||
|
||||
@ -3306,43 +3306,43 @@ For options that need extra arguments,
|
||||
they are listed after the option.
|
||||
@description{
|
||||
|
||||
@item{@id{LUA_GCCOLLECT}|
|
||||
@item{@defid{LUA_GCCOLLECT}|
|
||||
Performs a full garbage-collection cycle.
|
||||
}
|
||||
|
||||
@item{@id{LUA_GCSTOP}|
|
||||
@item{@defid{LUA_GCSTOP}|
|
||||
Stops the garbage collector.
|
||||
}
|
||||
|
||||
@item{@id{LUA_GCRESTART}|
|
||||
@item{@defid{LUA_GCRESTART}|
|
||||
Restarts the garbage collector.
|
||||
}
|
||||
|
||||
@item{@id{LUA_GCCOUNT}|
|
||||
@item{@defid{LUA_GCCOUNT}|
|
||||
Returns the current amount of memory (in Kbytes) in use by Lua.
|
||||
}
|
||||
|
||||
@item{@id{LUA_GCCOUNTB}|
|
||||
@item{@defid{LUA_GCCOUNTB}|
|
||||
Returns the remainder of dividing the current amount of bytes of
|
||||
memory in use by Lua by 1024.
|
||||
}
|
||||
|
||||
@item{@id{LUA_GCSTEP}|
|
||||
@item{@defid{LUA_GCSTEP}|
|
||||
Performs a step of garbage collection.
|
||||
}
|
||||
|
||||
@item{@id{LUA_GCISRUNNING}|
|
||||
@item{@defid{LUA_GCISRUNNING}|
|
||||
Returns a boolean that tells whether the collector is running
|
||||
(i.e., not stopped).
|
||||
}
|
||||
|
||||
@item{@id{LUA_GCINC} (int pause, int stepmul, int stepsize)|
|
||||
@item{@defid{LUA_GCINC} (int pause, int stepmul, int stepsize)|
|
||||
Changes the collector to incremental mode
|
||||
with the given parameters @see{incmode}.
|
||||
Returns the previous mode (@id{LUA_GCGEN} or @id{LUA_GCINC}).
|
||||
}
|
||||
|
||||
@item{@id{LUA_GCGEN} (int minormul, int minormajor, int majorminor)|
|
||||
@item{@defid{LUA_GCGEN} (int minormul, int minormajor, int majorminor)|
|
||||
Changes the collector to generational mode
|
||||
with the given parameters @see{genmode}.
|
||||
Returns the previous mode (@id{LUA_GCGEN} or @id{LUA_GCINC}).
|
||||
|
@ -162,9 +162,16 @@ end
|
||||
assert(collectgarbage'isrunning')
|
||||
|
||||
|
||||
do print"testing stop-the-world collection"
|
||||
collectgarbage("incremental", nil, 0)
|
||||
|
||||
-- just to make sure
|
||||
assert(collectgarbage'isrunning')
|
||||
-- each step does a complete cycle
|
||||
assert(collectgarbage("step"))
|
||||
assert(collectgarbage("step"))
|
||||
|
||||
-- back to default value
|
||||
collectgarbage("incremental", nil, 200)
|
||||
end
|
||||
|
||||
collectgarbage(oldmode)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user