|
@ -1,5 +1,5 @@ |
|
|
/*
|
|
|
/*
|
|
|
** $Id: lgc.c,v 2.174 2014/02/14 16:43:14 roberto Exp roberto $ |
|
|
** $Id: lgc.c,v 2.175 2014/02/15 13:12:01 roberto Exp roberto $ |
|
|
** Garbage Collector |
|
|
** Garbage Collector |
|
|
** See Copyright Notice in lua.h |
|
|
** See Copyright Notice in lua.h |
|
|
*/ |
|
|
*/ |
|
@ -23,6 +23,11 @@ |
|
|
#include "ltm.h" |
|
|
#include "ltm.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
** internal state for collector while inside the atomic phase. The |
|
|
|
|
|
** collector should never be in this state while running regular code. |
|
|
|
|
|
*/ |
|
|
|
|
|
#define GCSinsideatomic (GCSpause + 1) |
|
|
|
|
|
|
|
|
/*
|
|
|
/*
|
|
|
** cost of sweeping one element (the size of a small object divided |
|
|
** cost of sweeping one element (the size of a small object divided |
|
@ -288,15 +293,21 @@ static void markbeingfnz (global_State *g) { |
|
|
/*
|
|
|
/*
|
|
|
** Mark all values stored in marked open upvalues from non-marked threads. |
|
|
** Mark all values stored in marked open upvalues from non-marked threads. |
|
|
** (Values from marked threads were already marked when traversing the |
|
|
** (Values from marked threads were already marked when traversing the |
|
|
** thread.) |
|
|
** thread.) Remove from the list threads that no longer have upvalues and |
|
|
|
|
|
** not-marked threads. |
|
|
*/ |
|
|
*/ |
|
|
static void remarkupvals (global_State *g) { |
|
|
static void remarkupvals (global_State *g) { |
|
|
GCObject *thread = g->mainthread->next; |
|
|
lua_State *thread; |
|
|
for (; thread != NULL; thread = gch(thread)->next) { |
|
|
lua_State **p = &g->twups; |
|
|
lua_assert(!isblack(thread)); /* threads are never black */ |
|
|
while ((thread = *p) != NULL) { |
|
|
if (!isgray(thread)) { /* dead thread? */ |
|
|
lua_assert(!isblack(obj2gco(thread))); /* threads are never black */ |
|
|
UpVal *uv = gco2th(thread)->openupval; |
|
|
if (isgray(obj2gco(thread)) && thread->openupval != NULL) |
|
|
for (; uv != NULL; uv = uv->u.open.next) { |
|
|
p = &thread->twups; /* keep marked thread with upvalues in the list */ |
|
|
|
|
|
else { /* thread is not marked or without upvalues */ |
|
|
|
|
|
UpVal *uv; |
|
|
|
|
|
*p = thread->twups; /* remove thread from the list */ |
|
|
|
|
|
thread->twups = thread; /* mark that it is out of list */ |
|
|
|
|
|
for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { |
|
|
if (uv->u.open.touched) { |
|
|
if (uv->u.open.touched) { |
|
|
markvalue(g, uv->v); /* remark upvalue's value */ |
|
|
markvalue(g, uv->v); /* remark upvalue's value */ |
|
|
uv->u.open.touched = 0; |
|
|
uv->u.open.touched = 0; |
|
@ -459,13 +470,19 @@ static lu_mem traverseCclosure (global_State *g, CClosure *cl) { |
|
|
return sizeCclosure(cl->nupvalues); |
|
|
return sizeCclosure(cl->nupvalues); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
** open upvalues point to values in a thread, so those values should |
|
|
|
|
|
** be marked when the thread is traversed except in the atomic phase |
|
|
|
|
|
** (because then the value cannot be changed by the thread and the |
|
|
|
|
|
** thread may not be traversed again) |
|
|
|
|
|
*/ |
|
|
static lu_mem traverseLclosure (global_State *g, LClosure *cl) { |
|
|
static lu_mem traverseLclosure (global_State *g, LClosure *cl) { |
|
|
int i; |
|
|
int i; |
|
|
markobject(g, cl->p); /* mark its prototype */ |
|
|
markobject(g, cl->p); /* mark its prototype */ |
|
|
for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ |
|
|
for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ |
|
|
UpVal *uv = cl->upvals[i]; |
|
|
UpVal *uv = cl->upvals[i]; |
|
|
if (uv != NULL) { |
|
|
if (uv != NULL) { |
|
|
if (upisopen(uv)) |
|
|
if (upisopen(uv) && g->gcstate != GCSinsideatomic) |
|
|
uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ |
|
|
uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ |
|
|
else |
|
|
else |
|
|
markvalue(g, uv->v); |
|
|
markvalue(g, uv->v); |
|
@ -480,12 +497,19 @@ static lu_mem traversestack (global_State *g, lua_State *th) { |
|
|
StkId o = th->stack; |
|
|
StkId o = th->stack; |
|
|
if (o == NULL) |
|
|
if (o == NULL) |
|
|
return 1; /* stack not completely built yet */ |
|
|
return 1; /* stack not completely built yet */ |
|
|
|
|
|
lua_assert(g->gcstate == GCSinsideatomic || |
|
|
|
|
|
th->openupval == NULL || isintwups(th)); |
|
|
for (; o < th->top; o++) /* mark live elements in the stack */ |
|
|
for (; o < th->top; o++) /* mark live elements in the stack */ |
|
|
markvalue(g, o); |
|
|
markvalue(g, o); |
|
|
if (g->gcstate == GCSatomic) { /* final traversal? */ |
|
|
if (g->gcstate == GCSinsideatomic) { /* final traversal? */ |
|
|
StkId lim = th->stack + th->stacksize; /* real end of stack */ |
|
|
StkId lim = th->stack + th->stacksize; /* real end of stack */ |
|
|
for (; o < lim; o++) /* clear not-marked stack slice */ |
|
|
for (; o < lim; o++) /* clear not-marked stack slice */ |
|
|
setnilvalue(o); |
|
|
setnilvalue(o); |
|
|
|
|
|
/* 'remarkupvals' may have removed thread from 'twups' list */ |
|
|
|
|
|
if (!isintwups(th) && th->openupval != NULL) { |
|
|
|
|
|
th->twups = g->twups; /* link it back to the list */ |
|
|
|
|
|
g->twups = th; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
CallInfo *ci; |
|
|
CallInfo *ci; |
|
@ -941,6 +965,7 @@ static l_mem atomic (lua_State *L) { |
|
|
l_mem work = -cast(l_mem, g->GCmemtrav); /* start counting work */ |
|
|
l_mem work = -cast(l_mem, g->GCmemtrav); /* start counting work */ |
|
|
GCObject *origweak, *origall; |
|
|
GCObject *origweak, *origall; |
|
|
lua_assert(!iswhite(obj2gco(g->mainthread))); |
|
|
lua_assert(!iswhite(obj2gco(g->mainthread))); |
|
|
|
|
|
g->gcstate = GCSinsideatomic; |
|
|
markobject(g, L); /* mark running thread */ |
|
|
markobject(g, L); /* mark running thread */ |
|
|
/* registry and global metatables may be changed by API */ |
|
|
/* registry and global metatables may be changed by API */ |
|
|
markvalue(g, &g->l_registry); |
|
|
markvalue(g, &g->l_registry); |
|
|