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; |