diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-08-03 13:22:57 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-08-03 13:22:57 -0300 |
| commit | b9b554e0f68726b19274209ea6ce910b7e9f5fbf (patch) | |
| tree | 5e0b126da4447bcb7bdfc80821d209dc86584a9e | |
| parent | 0dc5deca1c0182a4a3db2fcfd7bc721f27fb352b (diff) | |
| download | lua-b9b554e0f68726b19274209ea6ce910b7e9f5fbf.tar.gz lua-b9b554e0f68726b19274209ea6ce910b7e9f5fbf.tar.bz2 lua-b9b554e0f68726b19274209ea6ce910b7e9f5fbf.zip | |
Clearer handling of gray lists when entering generational mode
When entering generational mode, all objects are old. So, the only
objects that need to be in a gray list are threads, which can be
assigned without barriers. Changes in anything else (e.g., weak
tables) will trigger barriers that, if needed, will add the object
to a gray list.
| -rw-r--r-- | lgc.c | 36 | ||||
| -rw-r--r-- | ltests.c | 60 | ||||
| -rw-r--r-- | ltests.h | 1 |
3 files changed, 79 insertions, 18 deletions
| @@ -368,12 +368,17 @@ static int remarkupvals (global_State *g) { | |||
| 368 | } | 368 | } |
| 369 | 369 | ||
| 370 | 370 | ||
| 371 | static void cleargraylists (global_State *g) { | ||
| 372 | g->gray = g->grayagain = NULL; | ||
| 373 | g->weak = g->allweak = g->ephemeron = NULL; | ||
| 374 | } | ||
| 375 | |||
| 376 | |||
| 371 | /* | 377 | /* |
| 372 | ** mark root set and reset all gray lists, to start a new collection | 378 | ** mark root set and reset all gray lists, to start a new collection |
| 373 | */ | 379 | */ |
| 374 | static void restartcollection (global_State *g) { | 380 | static void restartcollection (global_State *g) { |
| 375 | g->gray = g->grayagain = NULL; | 381 | cleargraylists(g); |
| 376 | g->weak = g->allweak = g->ephemeron = NULL; | ||
| 377 | markobject(g, g->mainthread); | 382 | markobject(g, g->mainthread); |
| 378 | markvalue(g, &g->l_registry); | 383 | markvalue(g, &g->l_registry); |
| 379 | markmt(g); | 384 | markmt(g); |
| @@ -1019,19 +1024,30 @@ static void setpause (global_State *g); | |||
| 1019 | 1024 | ||
| 1020 | 1025 | ||
| 1021 | /* | 1026 | /* |
| 1022 | ** Sweep a list of objects, deleting dead ones and turning | 1027 | ** Sweep a list of objects to enter generational mode. Deletes dead |
| 1023 | ** the non dead to old (without changing their colors). | 1028 | ** objects and turns the non dead to old. All non-dead threads---which |
| 1029 | ** are now old---must be in a gray list. Everything else is not in a | ||
| 1030 | ** gray list. | ||
| 1031 | ** | ||
| 1024 | */ | 1032 | */ |
| 1025 | static void sweep2old (lua_State *L, GCObject **p) { | 1033 | static void sweep2old (lua_State *L, GCObject **p) { |
| 1026 | GCObject *curr; | 1034 | GCObject *curr; |
| 1035 | global_State *g = G(L); | ||
| 1027 | while ((curr = *p) != NULL) { | 1036 | while ((curr = *p) != NULL) { |
| 1028 | if (iswhite(curr)) { /* is 'curr' dead? */ | 1037 | if (iswhite(curr)) { /* is 'curr' dead? */ |
| 1029 | lua_assert(isdead(G(L), curr)); | 1038 | lua_assert(isdead(g, curr)); |
| 1030 | *p = curr->next; /* remove 'curr' from list */ | 1039 | *p = curr->next; /* remove 'curr' from list */ |
| 1031 | freeobj(L, curr); /* erase 'curr' */ | 1040 | freeobj(L, curr); /* erase 'curr' */ |
| 1032 | } | 1041 | } |
| 1033 | else { /* all surviving objects become old */ | 1042 | else { /* all surviving objects become old */ |
| 1034 | setage(curr, G_OLD); | 1043 | setage(curr, G_OLD); |
| 1044 | if (curr->tt == LUA_VTHREAD) { /* threads must be watched */ | ||
| 1045 | lua_State *th = gco2th(curr); | ||
| 1046 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ | ||
| 1047 | black2gray(th); /* OK if already gray */ | ||
| 1048 | } | ||
| 1049 | else /* everything else is black */ | ||
| 1050 | gray2black(curr); /* OK if already black */ | ||
| 1035 | p = &curr->next; /* go to next element */ | 1051 | p = &curr->next; /* go to next element */ |
| 1036 | } | 1052 | } |
| 1037 | } | 1053 | } |
| @@ -1221,7 +1237,14 @@ static void youngcollection (lua_State *L, global_State *g) { | |||
| 1221 | } | 1237 | } |
| 1222 | 1238 | ||
| 1223 | 1239 | ||
| 1240 | /* | ||
| 1241 | ** Clears all gray lists, sweeps objects, and prepare sublists to enter | ||
| 1242 | ** generational mode. The sweeps remove dead objects and turn all | ||
| 1243 | ** surviving objects to old. Threads go back to 'grayagain'; everything | ||
| 1244 | ** else is turned black (not in any gray list). | ||
| 1245 | */ | ||
| 1224 | static void atomic2gen (lua_State *L, global_State *g) { | 1246 | static void atomic2gen (lua_State *L, global_State *g) { |
| 1247 | cleargraylists(g); | ||
| 1225 | /* sweep all elements making them old */ | 1248 | /* sweep all elements making them old */ |
| 1226 | g->gcstate = GCSswpallgc; | 1249 | g->gcstate = GCSswpallgc; |
| 1227 | sweep2old(L, &g->allgc); | 1250 | sweep2old(L, &g->allgc); |
| @@ -1244,7 +1267,8 @@ static void atomic2gen (lua_State *L, global_State *g) { | |||
| 1244 | 1267 | ||
| 1245 | /* | 1268 | /* |
| 1246 | ** Enter generational mode. Must go until the end of an atomic cycle | 1269 | ** Enter generational mode. Must go until the end of an atomic cycle |
| 1247 | ** to ensure that all threads and weak tables are in the gray lists. | 1270 | ** to ensure that all objects are correctly marked and weak tables |
| 1271 | ** are cleared. | ||
| 1248 | ** Then, turn all objects into old and finishes the collection. | 1272 | ** Then, turn all objects into old and finishes the collection. |
| 1249 | */ | 1273 | */ |
| 1250 | static lu_mem entergen (lua_State *L, global_State *g) { | 1274 | static lu_mem entergen (lua_State *L, global_State *g) { |
| @@ -186,7 +186,8 @@ typedef union Header { | |||
| 186 | 186 | ||
| 187 | 187 | ||
| 188 | Memcontrol l_memcontrol = | 188 | Memcontrol l_memcontrol = |
| 189 | {0UL, 0UL, 0UL, 0UL, (~0UL), {0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}}; | 189 | {0, 0UL, 0UL, 0UL, 0UL, (~0UL), |
| 190 | {0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}}; | ||
| 190 | 191 | ||
| 191 | 192 | ||
| 192 | static void freeblock (Memcontrol *mc, Header *block) { | 193 | static void freeblock (Memcontrol *mc, Header *block) { |
| @@ -225,6 +226,10 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) { | |||
| 225 | freeblock(mc, block); | 226 | freeblock(mc, block); |
| 226 | return NULL; | 227 | return NULL; |
| 227 | } | 228 | } |
| 229 | if (mc->failnext) { | ||
| 230 | mc->failnext = 0; | ||
| 231 | return NULL; /* fake a single memory allocation error */ | ||
| 232 | } | ||
| 228 | if (mc->countlimit != ~0UL && size != oldsize) { /* count limit in use? */ | 233 | if (mc->countlimit != ~0UL && size != oldsize) { /* count limit in use? */ |
| 229 | if (mc->countlimit == 0) | 234 | if (mc->countlimit == 0) |
| 230 | return NULL; /* fake a memory allocation error */ | 235 | return NULL; /* fake a memory allocation error */ |
| @@ -513,10 +518,12 @@ static void checkobject (global_State *g, GCObject *o, int maybedead, | |||
| 513 | } | 518 | } |
| 514 | 519 | ||
| 515 | 520 | ||
| 516 | static void checkgraylist (global_State *g, GCObject *o) { | 521 | static lu_mem checkgraylist (global_State *g, GCObject *o) { |
| 522 | int total = 0; /* count number of elements in the list */ | ||
| 517 | ((void)g); /* better to keep it available if we need to print an object */ | 523 | ((void)g); /* better to keep it available if we need to print an object */ |
| 518 | while (o) { | 524 | while (o) { |
| 519 | lua_assert(isgray(o) || getage(o) == G_TOUCHED2); | 525 | lua_assert(isgray(o) || getage(o) == G_TOUCHED2); |
| 526 | total++; | ||
| 520 | switch (o->tt) { | 527 | switch (o->tt) { |
| 521 | case LUA_VTABLE: o = gco2t(o)->gclist; break; | 528 | case LUA_VTABLE: o = gco2t(o)->gclist; break; |
| 522 | case LUA_VLCL: o = gco2lcl(o)->gclist; break; | 529 | case LUA_VLCL: o = gco2lcl(o)->gclist; break; |
| @@ -530,40 +537,54 @@ static void checkgraylist (global_State *g, GCObject *o) { | |||
| 530 | default: lua_assert(0); /* other objects cannot be in a gray list */ | 537 | default: lua_assert(0); /* other objects cannot be in a gray list */ |
| 531 | } | 538 | } |
| 532 | } | 539 | } |
| 540 | return total; | ||
| 533 | } | 541 | } |
| 534 | 542 | ||
| 535 | 543 | ||
| 536 | /* | 544 | /* |
| 537 | ** Check objects in gray lists. | 545 | ** Check objects in gray lists. |
| 538 | */ | 546 | */ |
| 539 | static void checkgrays (global_State *g) { | 547 | static lu_mem checkgrays (global_State *g) { |
| 540 | if (!keepinvariant(g)) return; | 548 | int total = 0; /* count number of elements in all lists */ |
| 541 | checkgraylist(g, g->gray); | 549 | if (!keepinvariant(g)) return total; |
| 542 | checkgraylist(g, g->grayagain); | 550 | total += checkgraylist(g, g->gray); |
| 543 | checkgraylist(g, g->weak); | 551 | total += checkgraylist(g, g->grayagain); |
| 544 | checkgraylist(g, g->ephemeron); | 552 | total += checkgraylist(g, g->weak); |
| 553 | total += checkgraylist(g, g->allweak); | ||
| 554 | total += checkgraylist(g, g->ephemeron); | ||
| 555 | return total; | ||
| 545 | } | 556 | } |
| 546 | 557 | ||
| 547 | 558 | ||
| 548 | static void checklist (global_State *g, int maybedead, int tof, | 559 | /* Increment 't' if 'o' should be in a gray list */ |
| 560 | #define incifingray(o,t) \ | ||
| 561 | if (isgray(o) || getage(o) == G_TOUCHED2) (t)++ | ||
| 562 | |||
| 563 | static lu_mem checklist (global_State *g, int maybedead, int tof, | ||
| 549 | GCObject *newl, GCObject *survival, GCObject *old, GCObject *reallyold) { | 564 | GCObject *newl, GCObject *survival, GCObject *old, GCObject *reallyold) { |
| 550 | GCObject *o; | 565 | GCObject *o; |
| 566 | lu_mem total = 0; /* number of object that should be in gray lists */ | ||
| 551 | for (o = newl; o != survival; o = o->next) { | 567 | for (o = newl; o != survival; o = o->next) { |
| 552 | checkobject(g, o, maybedead, G_NEW); | 568 | checkobject(g, o, maybedead, G_NEW); |
| 569 | incifingray(o, total); | ||
| 553 | lua_assert(!tof == !tofinalize(o)); | 570 | lua_assert(!tof == !tofinalize(o)); |
| 554 | } | 571 | } |
| 555 | for (o = survival; o != old; o = o->next) { | 572 | for (o = survival; o != old; o = o->next) { |
| 556 | checkobject(g, o, 0, G_SURVIVAL); | 573 | checkobject(g, o, 0, G_SURVIVAL); |
| 574 | incifingray(o, total); | ||
| 557 | lua_assert(!tof == !tofinalize(o)); | 575 | lua_assert(!tof == !tofinalize(o)); |
| 558 | } | 576 | } |
| 559 | for (o = old; o != reallyold; o = o->next) { | 577 | for (o = old; o != reallyold; o = o->next) { |
| 560 | checkobject(g, o, 0, G_OLD1); | 578 | checkobject(g, o, 0, G_OLD1); |
| 579 | incifingray(o, total); | ||
| 561 | lua_assert(!tof == !tofinalize(o)); | 580 | lua_assert(!tof == !tofinalize(o)); |
| 562 | } | 581 | } |
| 563 | for (o = reallyold; o != NULL; o = o->next) { | 582 | for (o = reallyold; o != NULL; o = o->next) { |
| 564 | checkobject(g, o, 0, G_OLD); | 583 | checkobject(g, o, 0, G_OLD); |
| 584 | incifingray(o, total); | ||
| 565 | lua_assert(!tof == !tofinalize(o)); | 585 | lua_assert(!tof == !tofinalize(o)); |
| 566 | } | 586 | } |
| 587 | return total; | ||
| 567 | } | 588 | } |
| 568 | 589 | ||
| 569 | 590 | ||
| @@ -571,13 +592,15 @@ int lua_checkmemory (lua_State *L) { | |||
| 571 | global_State *g = G(L); | 592 | global_State *g = G(L); |
| 572 | GCObject *o; | 593 | GCObject *o; |
| 573 | int maybedead; | 594 | int maybedead; |
| 595 | lu_mem totalin; /* total of objects that are in gray lists */ | ||
| 596 | lu_mem totalshould; /* total of objects that should be in gray lists */ | ||
| 574 | if (keepinvariant(g)) { | 597 | if (keepinvariant(g)) { |
| 575 | lua_assert(!iswhite(g->mainthread)); | 598 | lua_assert(!iswhite(g->mainthread)); |
| 576 | lua_assert(!iswhite(gcvalue(&g->l_registry))); | 599 | lua_assert(!iswhite(gcvalue(&g->l_registry))); |
| 577 | } | 600 | } |
| 578 | lua_assert(!isdead(g, gcvalue(&g->l_registry))); | 601 | lua_assert(!isdead(g, gcvalue(&g->l_registry))); |
| 579 | lua_assert(g->sweepgc == NULL || issweepphase(g)); | 602 | lua_assert(g->sweepgc == NULL || issweepphase(g)); |
| 580 | checkgrays(g); | 603 | totalin = checkgrays(g); |
| 581 | 604 | ||
| 582 | /* check 'fixedgc' list */ | 605 | /* check 'fixedgc' list */ |
| 583 | for (o = g->fixedgc; o != NULL; o = o->next) { | 606 | for (o = g->fixedgc; o != NULL; o = o->next) { |
| @@ -586,17 +609,22 @@ int lua_checkmemory (lua_State *L) { | |||
| 586 | 609 | ||
| 587 | /* check 'allgc' list */ | 610 | /* check 'allgc' list */ |
| 588 | maybedead = (GCSatomic < g->gcstate && g->gcstate <= GCSswpallgc); | 611 | maybedead = (GCSatomic < g->gcstate && g->gcstate <= GCSswpallgc); |
| 589 | checklist(g, maybedead, 0, g->allgc, g->survival, g->old1, g->reallyold); | 612 | totalshould = checklist(g, maybedead, 0, g->allgc, |
| 613 | g->survival, g->old1, g->reallyold); | ||
| 590 | 614 | ||
| 591 | /* check 'finobj' list */ | 615 | /* check 'finobj' list */ |
| 592 | checklist(g, 0, 1, g->finobj, g->finobjsur, g->finobjold1, g->finobjrold); | 616 | totalshould += checklist(g, 0, 1, g->finobj, |
| 617 | g->finobjsur, g->finobjold1, g->finobjrold); | ||
| 593 | 618 | ||
| 594 | /* check 'tobefnz' list */ | 619 | /* check 'tobefnz' list */ |
| 595 | for (o = g->tobefnz; o != NULL; o = o->next) { | 620 | for (o = g->tobefnz; o != NULL; o = o->next) { |
| 596 | checkobject(g, o, 0, G_NEW); | 621 | checkobject(g, o, 0, G_NEW); |
| 622 | incifingray(o, totalshould); | ||
| 597 | lua_assert(tofinalize(o)); | 623 | lua_assert(tofinalize(o)); |
| 598 | lua_assert(o->tt == LUA_VUSERDATA || o->tt == LUA_VTABLE); | 624 | lua_assert(o->tt == LUA_VUSERDATA || o->tt == LUA_VTABLE); |
| 599 | } | 625 | } |
| 626 | if (keepinvariant(g)) | ||
| 627 | lua_assert(totalin == totalshould); | ||
| 600 | return 0; | 628 | return 0; |
| 601 | } | 629 | } |
| 602 | 630 | ||
| @@ -807,6 +835,13 @@ static int alloc_count (lua_State *L) { | |||
| 807 | l_memcontrol.countlimit = luaL_checkinteger(L, 1); | 835 | l_memcontrol.countlimit = luaL_checkinteger(L, 1); |
| 808 | return 0; | 836 | return 0; |
| 809 | } | 837 | } |
| 838 | |||
| 839 | |||
| 840 | static int alloc_failnext (lua_State *L) { | ||
| 841 | UNUSED(L); | ||
| 842 | l_memcontrol.failnext = 1; | ||
| 843 | return 0; | ||
| 844 | } | ||
| 810 | 845 | ||
| 811 | 846 | ||
| 812 | static int settrick (lua_State *L) { | 847 | static int settrick (lua_State *L) { |
| @@ -1864,6 +1899,7 @@ static const struct luaL_Reg tests_funcs[] = { | |||
| 1864 | {"makeCfunc", makeCfunc}, | 1899 | {"makeCfunc", makeCfunc}, |
| 1865 | {"totalmem", mem_query}, | 1900 | {"totalmem", mem_query}, |
| 1866 | {"alloccount", alloc_count}, | 1901 | {"alloccount", alloc_count}, |
| 1902 | {"allocfailnext", alloc_failnext}, | ||
| 1867 | {"trick", settrick}, | 1903 | {"trick", settrick}, |
| 1868 | {"udataval", udataval}, | 1904 | {"udataval", udataval}, |
| 1869 | {"unref", unref}, | 1905 | {"unref", unref}, |
| @@ -51,6 +51,7 @@ | |||
| 51 | 51 | ||
| 52 | /* memory-allocator control variables */ | 52 | /* memory-allocator control variables */ |
| 53 | typedef struct Memcontrol { | 53 | typedef struct Memcontrol { |
| 54 | int failnext; | ||
| 54 | unsigned long numblocks; | 55 | unsigned long numblocks; |
| 55 | unsigned long total; | 56 | unsigned long total; |
| 56 | unsigned long maxmem; | 57 | unsigned long maxmem; |
