aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2014-09-03 13:54:41 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2014-09-03 13:54:41 -0300
commit0a6b58c3aac2741cf1c84c44d168c73e3e478c4d (patch)
tree31e96d3c94b2c01c75820463298580b2f05a8f79
parent979a663d2a8200591e263c9a7643b7379b2d7ac9 (diff)
downloadlua-0a6b58c3aac2741cf1c84c44d168c73e3e478c4d.tar.gz
lua-0a6b58c3aac2741cf1c84c44d168c73e3e478c4d.tar.bz2
lua-0a6b58c3aac2741cf1c84c44d168c73e3e478c4d.zip
weak tables that must be retraversed are kept in 'grayagain' list
until atomic phase (instead of going to their special lists) + more comments
-rw-r--r--lgc.c80
1 files changed, 38 insertions, 42 deletions
diff --git a/lgc.c b/lgc.c
index 0182e604..5b9b1470 100644
--- a/lgc.c
+++ b/lgc.c
@@ -335,12 +335,18 @@ static void restartcollection (global_State *g) {
335** ======================================================= 335** =======================================================
336*/ 336*/
337 337
338/*
339** Traverse a table with weak values and link it to proper list. During
340** propagate phase, keep it in 'grayagain' list, to be revisited in the
341** atomic phase. In the atomic phase, if table has any white value,
342** put it in 'weak' list, to be cleared.
343*/
338static void traverseweakvalue (global_State *g, Table *h) { 344static void traverseweakvalue (global_State *g, Table *h) {
339 Node *n, *limit = gnodelast(h); 345 Node *n, *limit = gnodelast(h);
340 /* if there is array part, assume it may have white values (do not 346 /* if there is array part, assume it may have white values (it is not
341 traverse it just to check) */ 347 worth traversing it now just to check) */
342 int hasclears = (h->sizearray > 0); 348 int hasclears = (h->sizearray > 0);
343 for (n = gnode(h, 0); n < limit; n++) { 349 for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
344 checkdeadkey(n); 350 checkdeadkey(n);
345 if (ttisnil(gval(n))) /* entry is empty? */ 351 if (ttisnil(gval(n))) /* entry is empty? */
346 removeentry(n); /* remove it */ 352 removeentry(n); /* remove it */
@@ -351,20 +357,30 @@ static void traverseweakvalue (global_State *g, Table *h) {
351 hasclears = 1; /* table will have to be cleared */ 357 hasclears = 1; /* table will have to be cleared */
352 } 358 }
353 } 359 }
354 if (hasclears) 360 if (g->gcstate == GCSpropagate)
361 linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */
362 else if (hasclears)
355 linkgclist(h, g->weak); /* has to be cleared later */ 363 linkgclist(h, g->weak); /* has to be cleared later */
356 else /* no white values */
357 linkgclist(h, g->grayagain); /* no need to clean */
358} 364}
359 365
360 366
367/*
368** Traverse an ephemeron table and link it to proper list. Returns true
369** iff any object was marked during this traversal (which implies that
370** convergence has to continue). During propagation phase, keep table
371** in 'grayagain' list, to be visited again in the atomic phase. In
372** the atomic phase, if table has any white->white entry, it has to
373** be revisited during ephemeron convergence (as that key may turn
374** black). Otherwise, if it has any white key, table has to be cleared
375** (in the atomic phase).
376*/
361static int traverseephemeron (global_State *g, Table *h) { 377static int traverseephemeron (global_State *g, Table *h) {
362 int marked = 0; /* true if an object is marked in this traversal */ 378 int marked = 0; /* true if an object is marked in this traversal */
363 int hasclears = 0; /* true if table has white keys */ 379 int hasclears = 0; /* true if table has white keys */
364 int prop = 0; /* true if table has entry "white-key -> white-value" */ 380 int hasww = 0; /* true if table has entry "white-key -> white-value" */
365 Node *n, *limit = gnodelast(h); 381 Node *n, *limit = gnodelast(h);
366 int i; 382 int i;
367 /* traverse array part (numeric keys are 'strong') */ 383 /* traverse array part */
368 for (i = 0; i < h->sizearray; i++) { 384 for (i = 0; i < h->sizearray; i++) {
369 if (valiswhite(&h->array[i])) { 385 if (valiswhite(&h->array[i])) {
370 marked = 1; 386 marked = 1;
@@ -379,19 +395,20 @@ static int traverseephemeron (global_State *g, Table *h) {
379 else if (iscleared(g, gkey(n))) { /* key is not marked (yet)? */ 395 else if (iscleared(g, gkey(n))) { /* key is not marked (yet)? */
380 hasclears = 1; /* table must be cleared */ 396 hasclears = 1; /* table must be cleared */
381 if (valiswhite(gval(n))) /* value not marked yet? */ 397 if (valiswhite(gval(n))) /* value not marked yet? */
382 prop = 1; /* must propagate again */ 398 hasww = 1; /* white-white entry */
383 } 399 }
384 else if (valiswhite(gval(n))) { /* value not marked yet? */ 400 else if (valiswhite(gval(n))) { /* value not marked yet? */
385 marked = 1; 401 marked = 1;
386 reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ 402 reallymarkobject(g, gcvalue(gval(n))); /* mark it now */
387 } 403 }
388 } 404 }
389 if (prop) 405 /* link table into proper list */
406 if (g->gcstate == GCSpropagate)
407 linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */
408 else if (hasww) /* table has white->white entries? */
390 linkgclist(h, g->ephemeron); /* have to propagate again */ 409 linkgclist(h, g->ephemeron); /* have to propagate again */
391 else if (hasclears) /* does table have white keys? */ 410 else if (hasclears) /* table has white keys? */
392 linkgclist(h, g->allweak); /* may have to clean white keys */ 411 linkgclist(h, g->allweak); /* may have to clean white keys */
393 else /* no white keys */
394 linkgclist(h, g->grayagain); /* no need to clean */
395 return marked; 412 return marked;
396} 413}
397 414
@@ -565,35 +582,12 @@ static void propagateall (global_State *g) {
565} 582}
566 583
567 584
568static void propagatelist (global_State *g, GCObject *l) {
569 lua_assert(g->gray == NULL); /* no grays left */
570 g->gray = l;
571 propagateall(g); /* traverse all elements from 'l' */
572}
573
574/*
575** retraverse all gray lists. Because tables may be reinserted in other
576** lists when traversed, traverse the original lists to avoid traversing
577** twice the same table (which is not wrong, but inefficient)
578*/
579static void retraversegrays (global_State *g) {
580 GCObject *weak = g->weak; /* save original lists */
581 GCObject *grayagain = g->grayagain;
582 GCObject *ephemeron = g->ephemeron;
583 g->weak = g->grayagain = g->ephemeron = NULL;
584 propagateall(g); /* traverse main gray list */
585 propagatelist(g, grayagain);
586 propagatelist(g, weak);
587 propagatelist(g, ephemeron);
588}
589
590
591static void convergeephemerons (global_State *g) { 585static void convergeephemerons (global_State *g) {
592 int changed; 586 int changed;
593 do { 587 do {
594 GCObject *w; 588 GCObject *w;
595 GCObject *next = g->ephemeron; /* get ephemeron list */ 589 GCObject *next = g->ephemeron; /* get ephemeron list */
596 g->ephemeron = NULL; /* tables will return to this list when traversed */ 590 g->ephemeron = NULL; /* tables may return to this list when traversed */
597 changed = 0; 591 changed = 0;
598 while ((w = next) != NULL) { 592 while ((w = next) != NULL) {
599 next = gco2t(w)->gclist; 593 next = gco2t(w)->gclist;
@@ -966,19 +960,21 @@ static l_mem atomic (lua_State *L) {
966 global_State *g = G(L); 960 global_State *g = G(L);
967 l_mem work; 961 l_mem work;
968 GCObject *origweak, *origall; 962 GCObject *origweak, *origall;
969 g->GCmemtrav = 0; /* start counting work */ 963 GCObject *grayagain = g->grayagain; /* save original list */
964 lua_assert(g->ephemeron == NULL && g->weak == NULL);
970 lua_assert(!iswhite(g->mainthread)); 965 lua_assert(!iswhite(g->mainthread));
971 g->gcstate = GCSinsideatomic; 966 g->gcstate = GCSinsideatomic;
967 g->GCmemtrav = 0; /* start counting work */
972 markobject(g, L); /* mark running thread */ 968 markobject(g, L); /* mark running thread */
973 /* registry and global metatables may be changed by API */ 969 /* registry and global metatables may be changed by API */
974 markvalue(g, &g->l_registry); 970 markvalue(g, &g->l_registry);
975 markmt(g); /* mark basic metatables */ 971 markmt(g); /* mark global metatables */
976 /* remark occasional upvalues of (maybe) dead threads */ 972 /* remark occasional upvalues of (maybe) dead threads */
977 remarkupvals(g); 973 remarkupvals(g);
978 propagateall(g); /* propagate changes */ 974 propagateall(g); /* propagate changes */
979 work = g->GCmemtrav; /* stop counting (do not (re)count grays) */ 975 work = g->GCmemtrav; /* stop counting (do not recount gray-agains) */
980 /* traverse objects caught by write barrier and by 'remarkupvals' */ 976 g->gray = grayagain;
981 retraversegrays(g); 977 propagateall(g); /* traverse 'grayagain' list */
982 g->GCmemtrav = 0; /* restart counting */ 978 g->GCmemtrav = 0; /* restart counting */
983 convergeephemerons(g); 979 convergeephemerons(g);
984 /* at this point, all strongly accessible objects are marked. */ 980 /* at this point, all strongly accessible objects are marked. */