diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2014-09-03 13:54:41 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2014-09-03 13:54:41 -0300 |
commit | 0a6b58c3aac2741cf1c84c44d168c73e3e478c4d (patch) | |
tree | 31e96d3c94b2c01c75820463298580b2f05a8f79 | |
parent | 979a663d2a8200591e263c9a7643b7379b2d7ac9 (diff) | |
download | lua-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.c | 80 |
1 files changed, 38 insertions, 42 deletions
@@ -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 | */ | ||
338 | static void traverseweakvalue (global_State *g, Table *h) { | 344 | static 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 | */ | ||
361 | static int traverseephemeron (global_State *g, Table *h) { | 377 | static 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 | ||
568 | static 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 | */ | ||
579 | static 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 | |||
591 | static void convergeephemerons (global_State *g) { | 585 | static 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. */ |