aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-08-03 13:22:57 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-08-03 13:22:57 -0300
commitb9b554e0f68726b19274209ea6ce910b7e9f5fbf (patch)
tree5e0b126da4447bcb7bdfc80821d209dc86584a9e
parent0dc5deca1c0182a4a3db2fcfd7bc721f27fb352b (diff)
downloadlua-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.c36
-rw-r--r--ltests.c60
-rw-r--r--ltests.h1
3 files changed, 79 insertions, 18 deletions
diff --git a/lgc.c b/lgc.c
index 9973c9db..5e8c02d3 100644
--- a/lgc.c
+++ b/lgc.c
@@ -368,12 +368,17 @@ static int remarkupvals (global_State *g) {
368} 368}
369 369
370 370
371static 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*/
374static void restartcollection (global_State *g) { 380static 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*/
1025static void sweep2old (lua_State *L, GCObject **p) { 1033static 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*/
1224static void atomic2gen (lua_State *L, global_State *g) { 1246static 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*/
1250static lu_mem entergen (lua_State *L, global_State *g) { 1274static lu_mem entergen (lua_State *L, global_State *g) {
diff --git a/ltests.c b/ltests.c
index a13714d6..c0421781 100644
--- a/ltests.c
+++ b/ltests.c
@@ -186,7 +186,8 @@ typedef union Header {
186 186
187 187
188Memcontrol l_memcontrol = 188Memcontrol 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
192static void freeblock (Memcontrol *mc, Header *block) { 193static 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
516static void checkgraylist (global_State *g, GCObject *o) { 521static 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*/
539static void checkgrays (global_State *g) { 547static 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
548static 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
563static 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
840static int alloc_failnext (lua_State *L) {
841 UNUSED(L);
842 l_memcontrol.failnext = 1;
843 return 0;
844}
810 845
811 846
812static int settrick (lua_State *L) { 847static 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},
diff --git a/ltests.h b/ltests.h
index 1a2d8d28..e9219e29 100644
--- a/ltests.h
+++ b/ltests.h
@@ -51,6 +51,7 @@
51 51
52/* memory-allocator control variables */ 52/* memory-allocator control variables */
53typedef struct Memcontrol { 53typedef 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;