diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-04-05 13:50:51 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-04-05 13:50:51 -0300 |
commit | e4287da3a6b0b167da465fd449e5191b9ac9ef46 (patch) | |
tree | 3568d8717dea66ff815475a945cfaafc7e6d98da | |
parent | 1a1b2f3d7f321dd6f28118c985986940b189c635 (diff) | |
download | lua-e4287da3a6b0b167da465fd449e5191b9ac9ef46.tar.gz lua-e4287da3a6b0b167da465fd449e5191b9ac9ef46.tar.bz2 lua-e4287da3a6b0b167da465fd449e5191b9ac9ef46.zip |
generational collector (still not complete)
-rw-r--r-- | lgc.c | 359 | ||||
-rw-r--r-- | lgc.h | 54 | ||||
-rw-r--r-- | lstate.c | 5 | ||||
-rw-r--r-- | lstate.h | 8 |
4 files changed, 324 insertions, 102 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lgc.c,v 2.215 2016/12/22 13:08:50 roberto Exp roberto $ | 2 | ** $Id: lgc.c,v 2.216 2017/02/23 21:07:34 roberto Exp roberto $ |
3 | ** Garbage Collector | 3 | ** Garbage Collector |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -9,7 +9,7 @@ | |||
9 | 9 | ||
10 | #include "lprefix.h" | 10 | #include "lprefix.h" |
11 | 11 | ||
12 | 12 | #include <stdio.h> | |
13 | #include <string.h> | 13 | #include <string.h> |
14 | 14 | ||
15 | #include "lua.h" | 15 | #include "lua.h" |
@@ -27,12 +27,6 @@ | |||
27 | 27 | ||
28 | 28 | ||
29 | /* | 29 | /* |
30 | ** internal state for collector while inside the atomic phase. The | ||
31 | ** collector should never be in this state while running regular code. | ||
32 | */ | ||
33 | #define GCSinsideatomic (GCSpause + 1) | ||
34 | |||
35 | /* | ||
36 | ** cost of sweeping one element (the size of a small object divided | 30 | ** cost of sweeping one element (the size of a small object divided |
37 | ** by some adjust for the sweep speed) | 31 | ** by some adjust for the sweep speed) |
38 | */ | 32 | */ |
@@ -59,8 +53,9 @@ | |||
59 | #define PAUSEADJ 100 | 53 | #define PAUSEADJ 100 |
60 | 54 | ||
61 | 55 | ||
62 | /* mask to erase all color bits */ | 56 | /* mask to erase all color bits (plus gen. related stuff) */ |
63 | #define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS)) | 57 | #define maskcolors (~(bitmask(BLACKBIT) | WHITEBITS | AGEBITS)) |
58 | |||
64 | 59 | ||
65 | /* macro to erase all color bits then sets only the current white bit */ | 60 | /* macro to erase all color bits then sets only the current white bit */ |
66 | #define makewhite(g,x) \ | 61 | #define makewhite(g,x) \ |
@@ -157,8 +152,10 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { | |||
157 | lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); | 152 | lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); |
158 | if (keepinvariant(g)) { /* must keep invariant? */ | 153 | if (keepinvariant(g)) { /* must keep invariant? */ |
159 | reallymarkobject(g, v); /* restore invariant */ | 154 | reallymarkobject(g, v); /* restore invariant */ |
160 | if (isold(o)) | 155 | if (isold(o)) { |
161 | l_setbit((v)->marked, OLDBIT); | 156 | lua_assert(!isold(v)); |
157 | setage(v, G_OLD0); | ||
158 | } | ||
162 | } | 159 | } |
163 | else { /* sweep phase */ | 160 | else { /* sweep phase */ |
164 | lua_assert(issweepphase(g)); | 161 | lua_assert(issweepphase(g)); |
@@ -174,8 +171,12 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { | |||
174 | void luaC_barrierback_ (lua_State *L, Table *t) { | 171 | void luaC_barrierback_ (lua_State *L, Table *t) { |
175 | global_State *g = G(L); | 172 | global_State *g = G(L); |
176 | lua_assert(isblack(t) && !isdead(g, t)); | 173 | lua_assert(isblack(t) && !isdead(g, t)); |
174 | lua_assert(issweepphase(g) || getage(t) != G_TOUCHED1); | ||
175 | lua_assert(g->gckind != KGC_GEN || isold(t)); | ||
176 | if (getage(t) != G_TOUCHED2) /* not already in gray list? */ | ||
177 | linkgclist(t, g->grayagain); /* link it in 'grayagain' */ | ||
177 | black2gray(t); /* make table gray (again) */ | 178 | black2gray(t); /* make table gray (again) */ |
178 | linkgclist(t, g->grayagain); | 179 | setage(t, G_TOUCHED1); /* touched in current cycle */ |
179 | } | 180 | } |
180 | 181 | ||
181 | 182 | ||
@@ -188,10 +189,10 @@ void luaC_barrierback_ (lua_State *L, Table *t) { | |||
188 | void luaC_upvalbarrier_ (lua_State *L, UpVal *uv) { | 189 | void luaC_upvalbarrier_ (lua_State *L, UpVal *uv) { |
189 | global_State *g = G(L); | 190 | global_State *g = G(L); |
190 | GCObject *o = gcvalue(uv->v); | 191 | GCObject *o = gcvalue(uv->v); |
191 | lua_assert(!upisopen(uv)); /* ensured by macro luaC_upvalbarrier */ | ||
192 | if (keepinvariant(g)) { | 192 | if (keepinvariant(g)) { |
193 | markobject(g, o); | 193 | markobject(g, o); |
194 | l_setbit((o)->marked, OLDBIT); | 194 | if (!isold(o)) |
195 | setage(o, G_OLD0); | ||
195 | } | 196 | } |
196 | } | 197 | } |
197 | 198 | ||
@@ -379,10 +380,10 @@ static void traverseweakvalue (global_State *g, Table *h) { | |||
379 | hasclears = 1; /* table will have to be cleared */ | 380 | hasclears = 1; /* table will have to be cleared */ |
380 | } | 381 | } |
381 | } | 382 | } |
382 | if (g->gcstate == GCSpropagate) | 383 | if (g->gcstate == GCSatomic && hasclears) |
383 | linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ | ||
384 | else if (hasclears) | ||
385 | linkgclist(h, g->weak); /* has to be cleared later */ | 384 | linkgclist(h, g->weak); /* has to be cleared later */ |
385 | else | ||
386 | linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ | ||
386 | } | 387 | } |
387 | 388 | ||
388 | 389 | ||
@@ -431,6 +432,8 @@ static int traverseephemeron (global_State *g, Table *h) { | |||
431 | linkgclist(h, g->ephemeron); /* have to propagate again */ | 432 | linkgclist(h, g->ephemeron); /* have to propagate again */ |
432 | else if (hasclears) /* table has white keys? */ | 433 | else if (hasclears) /* table has white keys? */ |
433 | linkgclist(h, g->allweak); /* may have to clean white keys */ | 434 | linkgclist(h, g->allweak); /* may have to clean white keys */ |
435 | else if (g->gckind == KGC_GEN) | ||
436 | linkgclist(h, g->grayagain); /* keep it in some list */ | ||
434 | return marked; | 437 | return marked; |
435 | } | 438 | } |
436 | 439 | ||
@@ -450,6 +453,10 @@ static void traversestrongtable (global_State *g, Table *h) { | |||
450 | markvalue(g, gval(n)); /* mark value */ | 453 | markvalue(g, gval(n)); /* mark value */ |
451 | } | 454 | } |
452 | } | 455 | } |
456 | if (g->gckind == KGC_GEN) { | ||
457 | linkgclist(h, g->grayagain); /* keep it in some gray list */ | ||
458 | black2gray(h); | ||
459 | } | ||
453 | } | 460 | } |
454 | 461 | ||
455 | 462 | ||
@@ -522,7 +529,7 @@ static lu_mem traverseLclosure (global_State *g, LClosure *cl) { | |||
522 | for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ | 529 | for (i = 0; i < cl->nupvalues; i++) { /* mark its upvalues */ |
523 | UpVal *uv = cl->upvals[i]; | 530 | UpVal *uv = cl->upvals[i]; |
524 | if (uv != NULL) { | 531 | if (uv != NULL) { |
525 | if (upisopen(uv) && g->gcstate != GCSinsideatomic) | 532 | if (upisopen(uv) && g->gcstate != GCSatomic) |
526 | uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ | 533 | uv->u.open.touched = 1; /* can be marked in 'remarkupvals' */ |
527 | else | 534 | else |
528 | markvalue(g, uv->v); | 535 | markvalue(g, uv->v); |
@@ -536,11 +543,11 @@ static lu_mem traversethread (global_State *g, lua_State *th) { | |||
536 | StkId o = th->stack; | 543 | StkId o = th->stack; |
537 | if (o == NULL) | 544 | if (o == NULL) |
538 | return 1; /* stack not completely built yet */ | 545 | return 1; /* stack not completely built yet */ |
539 | lua_assert(g->gcstate == GCSinsideatomic || | 546 | lua_assert(g->gcstate == GCSatomic || |
540 | th->openupval == NULL || isintwups(th)); | 547 | th->openupval == NULL || isintwups(th)); |
541 | for (; o < th->top; o++) /* mark live elements in the stack */ | 548 | for (; o < th->top; o++) /* mark live elements in the stack */ |
542 | markvalue(g, o); | 549 | markvalue(g, o); |
543 | if (g->gcstate == GCSinsideatomic) { /* final traversal? */ | 550 | if (g->gcstate == GCSatomic) { /* final traversal? */ |
544 | StkId lim = th->stack + th->stacksize; /* real end of stack */ | 551 | StkId lim = th->stack + th->stacksize; /* real end of stack */ |
545 | for (; o < lim; o++) /* clear not-marked stack slice */ | 552 | for (; o < lim; o++) /* clear not-marked stack slice */ |
546 | setnilvalue(o); | 553 | setnilvalue(o); |
@@ -564,7 +571,7 @@ static lu_mem traversethread (global_State *g, lua_State *th) { | |||
564 | static void propagatemark (global_State *g) { | 571 | static void propagatemark (global_State *g) { |
565 | lu_mem size; | 572 | lu_mem size; |
566 | GCObject *o = g->gray; | 573 | GCObject *o = g->gray; |
567 | lua_assert(isgray(o)); | 574 | lua_assert(ongraylist(o)); |
568 | gray2black(o); | 575 | gray2black(o); |
569 | switch (o->tt) { | 576 | switch (o->tt) { |
570 | case LUA_TTABLE: { | 577 | case LUA_TTABLE: { |
@@ -638,11 +645,10 @@ static void convergeephemerons (global_State *g) { | |||
638 | 645 | ||
639 | 646 | ||
640 | /* | 647 | /* |
641 | ** clear entries with unmarked keys from all weaktables in list 'l' up | 648 | ** clear entries with unmarked keys from all weaktables in list 'l' |
642 | ** to element 'f' | ||
643 | */ | 649 | */ |
644 | static void clearkeys (global_State *g, GCObject *l, GCObject *f) { | 650 | static void clearkeys (global_State *g, GCObject *l) { |
645 | for (; l != f; l = gco2t(l)->gclist) { | 651 | for (; l; l = gco2t(l)->gclist) { |
646 | Table *h = gco2t(l); | 652 | Table *h = gco2t(l); |
647 | Node *n, *limit = gnodelast(h); | 653 | Node *n, *limit = gnodelast(h); |
648 | for (n = gnode(h, 0); n < limit; n++) { | 654 | for (n = gnode(h, 0); n < limit; n++) { |
@@ -885,11 +891,13 @@ static void separatetobefnz (global_State *g, int all) { | |||
885 | GCObject *curr; | 891 | GCObject *curr; |
886 | GCObject **p = &g->finobj; | 892 | GCObject **p = &g->finobj; |
887 | GCObject **lastnext = findlast(&g->tobefnz); | 893 | GCObject **lastnext = findlast(&g->tobefnz); |
888 | while ((curr = *p) != NULL) { /* traverse all finalizable objects */ | 894 | while ((curr = *p) != g->finobjold) { /* traverse all finalizable objects */ |
889 | lua_assert(tofinalize(curr)); | 895 | lua_assert(tofinalize(curr)); |
890 | if (!(iswhite(curr) || all)) /* not being collected? */ | 896 | if (!(iswhite(curr) || all)) /* not being collected? */ |
891 | p = &curr->next; /* don't bother with it */ | 897 | p = &curr->next; /* don't bother with it */ |
892 | else { | 898 | else { |
899 | if (curr == g->finobjsur) | ||
900 | g->finobjsur = curr->next; | ||
893 | *p = curr->next; /* remove 'curr' from 'finobj' list */ | 901 | *p = curr->next; /* remove 'curr' from 'finobj' list */ |
894 | curr->next = *lastnext; /* link at the end of 'tobefnz' list */ | 902 | curr->next = *lastnext; /* link at the end of 'tobefnz' list */ |
895 | *lastnext = curr; | 903 | *lastnext = curr; |
@@ -915,6 +923,14 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { | |||
915 | if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ | 923 | if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ |
916 | g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ | 924 | g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ |
917 | } | 925 | } |
926 | else { | ||
927 | if (o == g->survival) | ||
928 | g->survival = o->next; | ||
929 | if (o == g->old) | ||
930 | g->old = o->next; | ||
931 | if (o == g->reallyold) | ||
932 | g->reallyold = o->next; | ||
933 | } | ||
918 | /* search for pointer pointing to 'o' */ | 934 | /* search for pointer pointing to 'o' */ |
919 | for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } | 935 | for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } |
920 | *p = o->next; /* remove 'o' from 'allgc' list */ | 936 | *p = o->next; /* remove 'o' from 'allgc' list */ |
@@ -934,31 +950,65 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { | |||
934 | */ | 950 | */ |
935 | 951 | ||
936 | 952 | ||
953 | /* mask to erase all color bits (not changing gen-related stuff) */ | ||
954 | #define maskgencolors (~(bitmask(BLACKBIT) | WHITEBITS)) | ||
955 | |||
937 | #if 0 | 956 | #if 0 |
938 | static int count (GCObject *p, GCObject *limit) { | 957 | static int count (GCObject *p, GCObject *limit) { |
939 | int res = 0; | 958 | int res = 0; |
940 | for (; p != NULL && p != limit; p = p->next) | 959 | for (; p != NULL && p != limit; p = p->next) { |
941 | res++; | 960 | res++; |
961 | } | ||
942 | return res; | 962 | return res; |
943 | } | 963 | } |
944 | #endif | 964 | #endif |
945 | 965 | ||
946 | 966 | ||
947 | static GCObject **sweepgen (lua_State *L, GCObject **p, GCObject *limit, | 967 | static void sweep2old (lua_State *L, GCObject **p) { |
948 | int zeromask, int onemask) { | 968 | GCObject *curr; |
949 | global_State *g = G(L); | 969 | while ((curr = *p) != NULL) { |
950 | int ow = otherwhite(g); | 970 | if (iswhite(curr)) { /* is 'curr' dead? */ |
971 | lua_assert(isdead(G(L), curr)); | ||
972 | *p = curr->next; /* remove 'curr' from list */ | ||
973 | freeobj(L, curr); /* erase 'curr' */ | ||
974 | } | ||
975 | else { /* all surviving objects become old */ | ||
976 | setage(curr, G_OLD); | ||
977 | p = &curr->next; /* go to next element */ | ||
978 | } | ||
979 | } | ||
980 | } | ||
981 | |||
982 | |||
983 | static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, | ||
984 | GCObject *limit) { | ||
985 | int white = luaC_white(g); | ||
951 | GCObject *curr; | 986 | GCObject *curr; |
952 | while ((curr = *p) != limit) { | 987 | while ((curr = *p) != limit) { |
953 | int marked = curr->marked; | 988 | int marked = curr->marked; |
954 | if (isdeadm(ow, marked)) { /* is 'curr' dead? */ | 989 | if (iswhite(curr)) { /* is 'curr' dead? */ |
955 | lua_assert(!isold(curr)); | 990 | lua_assert(!isold(curr) && !testbits(curr->marked, white)); |
956 | *p = curr->next; /* remove 'curr' from list */ | 991 | *p = curr->next; /* remove 'curr' from list */ |
957 | freeobj(L, curr); /* erase 'curr' */ | 992 | freeobj(L, curr); /* erase 'curr' */ |
958 | } | 993 | } |
959 | else { /* correct mark */ | 994 | else { /* correct mark and age */ |
960 | if (!isold(curr)) /* don't change old objects */ | 995 | switch (getage(curr)) { |
961 | curr->marked = cast_byte((marked & zeromask) | onemask); | 996 | case G_NEW: /* make white and go to next age */ |
997 | curr->marked = cast_byte((marked & maskgencolors) | white); | ||
998 | changeage(curr, G_NEW, G_SURVIVAL); | ||
999 | break; | ||
1000 | case G_SURVIVAL: /* go to next age */ | ||
1001 | changeage(curr, G_SURVIVAL, G_OLD1); | ||
1002 | break; | ||
1003 | case G_OLD0: /* go to next age */ | ||
1004 | changeage(curr, G_OLD0, G_OLD1); | ||
1005 | break; | ||
1006 | case G_OLD1: /* go to next age */ | ||
1007 | changeage(curr, G_OLD1, G_OLD); | ||
1008 | break; | ||
1009 | default: /* don't change 'old', 'touched1', and 'touched2' */ | ||
1010 | break; | ||
1011 | } | ||
962 | p = &curr->next; /* go to next element */ | 1012 | p = &curr->next; /* go to next element */ |
963 | } | 1013 | } |
964 | } | 1014 | } |
@@ -966,51 +1016,184 @@ static GCObject **sweepgen (lua_State *L, GCObject **p, GCObject *limit, | |||
966 | } | 1016 | } |
967 | 1017 | ||
968 | 1018 | ||
969 | static void startgencycle (lua_State *L, global_State *g) { | 1019 | static void whitelist (global_State *g, GCObject *p) { |
970 | propagateall(g); | 1020 | int white = luaC_white(g); |
971 | atomic(L); | 1021 | for (; p != NULL; p = p->next) |
1022 | p->marked = cast_byte((p->marked & maskcolors) | white); | ||
972 | } | 1023 | } |
973 | 1024 | ||
974 | 1025 | ||
975 | static void finishgencycle (lua_State *L, global_State *g, int mask) { | 1026 | static void finishgencycle (lua_State *L, global_State *g) { |
976 | sweepgen(L, &g->finobj, NULL, ~0, mask); | 1027 | // sweepgen(L, &g->tobefnz, ~0, mask); |
977 | sweepgen(L, &g->tobefnz, NULL, ~0, mask); | ||
978 | checkSizes(L, g); | 1028 | checkSizes(L, g); |
979 | g->gcstate = GCSpropagate; /* skip restart */ | 1029 | g->gcstate = GCSpropagate; /* skip restart */ |
980 | callallpendingfinalizers(L); | 1030 | callallpendingfinalizers(L); |
981 | } | 1031 | } |
982 | 1032 | ||
1033 | static void printgray (GCObject *o) { | ||
1034 | printf("gray: "); | ||
1035 | while (o) { | ||
1036 | printf("%p %d %02x ", (void*)o, o->tt, o->marked); | ||
1037 | switch (o->tt) { | ||
1038 | case LUA_TTABLE: { | ||
1039 | Table *h = gco2t(o); | ||
1040 | o = h->gclist; | ||
1041 | break; | ||
1042 | } | ||
1043 | case LUA_TLCL: { | ||
1044 | LClosure *cl = gco2lcl(o); | ||
1045 | o = cl->gclist; | ||
1046 | break; | ||
1047 | } | ||
1048 | case LUA_TCCL: { | ||
1049 | CClosure *cl = gco2ccl(o); | ||
1050 | o = cl->gclist; | ||
1051 | break; | ||
1052 | } | ||
1053 | case LUA_TTHREAD: { | ||
1054 | lua_State *th = gco2th(o); | ||
1055 | o = th->gclist; | ||
1056 | break; | ||
1057 | } | ||
1058 | case LUA_TPROTO: { | ||
1059 | Proto *p = gco2p(o); | ||
1060 | o = p->gclist; | ||
1061 | break; | ||
1062 | } | ||
1063 | default: lua_assert(0); return; | ||
1064 | } | ||
1065 | } | ||
1066 | printf("\n"); | ||
1067 | } | ||
1068 | |||
1069 | |||
1070 | |||
1071 | static GCObject **correctgraylist (GCObject **p) { | ||
1072 | GCObject *curr; | ||
1073 | while ((curr = *p) != NULL) { | ||
1074 | switch (curr->tt) { | ||
1075 | case LUA_TTABLE: { | ||
1076 | Table *h = gco2t(curr); | ||
1077 | if (getage(h) == G_TOUCHED1) { /* touched in this cycle? */ | ||
1078 | lua_assert(isgray(h)); | ||
1079 | gray2black(h); /* make it black, for next barrier */ | ||
1080 | changeage(h, G_TOUCHED1, G_TOUCHED2); | ||
1081 | p = &h->gclist; /* go to next element */ | ||
1082 | } | ||
1083 | else { | ||
1084 | if (!iswhite(h)) { | ||
1085 | lua_assert(isold(h)); | ||
1086 | if (getage(h) == G_TOUCHED2) | ||
1087 | changeage(h, G_TOUCHED2, G_OLD); | ||
1088 | gray2black(h); /* make it black */ | ||
1089 | } | ||
1090 | *p = h->gclist; /* remove 'curr' from gray list */ | ||
1091 | } | ||
1092 | break; | ||
1093 | } | ||
1094 | case LUA_TTHREAD: { | ||
1095 | lua_State *th = gco2th(curr); | ||
1096 | lua_assert(!isblack(th)); | ||
1097 | if (iswhite(th)) /* new object? */ | ||
1098 | *p = th->gclist; /* remove from gray list */ | ||
1099 | else /* old threads remain gray */ | ||
1100 | p = &th->gclist; /* go to next element */ | ||
1101 | break; | ||
1102 | } | ||
1103 | default: lua_assert(0); /* nothing more could be gray here */ | ||
1104 | } | ||
1105 | } | ||
1106 | return p; | ||
1107 | } | ||
1108 | |||
1109 | |||
1110 | static void correctgraylists (global_State *g) { | ||
1111 | GCObject **list = correctgraylist(&g->grayagain); | ||
1112 | *list = g->weak; g->weak = NULL; | ||
1113 | list = correctgraylist(list); | ||
1114 | *list = g->allweak; g->allweak = NULL; | ||
1115 | list = correctgraylist(list); | ||
1116 | *list = g->ephemeron; g->ephemeron = NULL; | ||
1117 | correctgraylist(list); | ||
1118 | } | ||
1119 | |||
1120 | |||
1121 | static void markold (global_State *g, GCObject *from, GCObject *to) { | ||
1122 | GCObject *p; | ||
1123 | for (p = from; p != to; p = p->next) { | ||
1124 | if (getage(p) == G_OLD1) { | ||
1125 | lua_assert(!iswhite(p)); | ||
1126 | if (isblack(p)) { | ||
1127 | black2gray(p); /* should be '2white', but gray works too */ | ||
1128 | reallymarkobject(g, p); | ||
1129 | } | ||
1130 | else | ||
1131 | lua_assert(p->tt == LUA_TTHREAD); /* threads are always gray */ | ||
1132 | } | ||
1133 | } | ||
1134 | } | ||
1135 | |||
983 | 1136 | ||
984 | static void youngcollection (lua_State *L, global_State *g) { | 1137 | static void youngcollection (lua_State *L, global_State *g) { |
985 | GCObject **psurvival; | 1138 | GCObject **psurvival; |
986 | lua_assert(g->gcstate == GCSpropagate); | 1139 | lua_assert(g->gcstate == GCSpropagate); |
987 | startgencycle(L, g); | 1140 | markold(g, g->survival, g->reallyold); |
1141 | markold(g, g->finobj, g->finobjrold); /* ??? */ | ||
1142 | atomic(L); | ||
1143 | |||
988 | /* sweep nursery */ | 1144 | /* sweep nursery */ |
989 | psurvival = sweepgen(L, &g->allgc, g->survival, maskcolors, luaC_white(g)); | 1145 | psurvival = sweepgen(L, g, &g->allgc, g->survival); |
990 | lua_assert(*psurvival == g->survival); | 1146 | /* sweep 'survival' and 'old' */ |
991 | /* sweep 'survival' list, making elements old */ | 1147 | sweepgen(L, g, psurvival, g->reallyold); |
992 | sweepgen(L, psurvival, g->old, ~0, bitmask(OLDBIT)); | 1148 | g->reallyold = g->old; |
993 | /* incorporate 'survival' list into old list */ | 1149 | g->old = *psurvival; /* 'survival' survivals are old now */ |
994 | g->old = *psurvival; | 1150 | g->survival = g->allgc; /* all news are survivals */ |
995 | /* surviving young objects go to 'survival' list */ | 1151 | |
996 | g->survival = g->allgc; | 1152 | /* repeat for 'finobj' lists */ |
997 | finishgencycle(L, g, 0); | 1153 | psurvival = sweepgen(L, g, &g->finobj, g->finobjsur); |
998 | lua_checkmemory(L); | 1154 | /* sweep 'survival' and 'old' */ |
1155 | sweepgen(L, g, psurvival, g->finobjrold); | ||
1156 | g->finobjrold = g->finobjold; | ||
1157 | g->finobjold = *psurvival; /* 'survival' survivals are old now */ | ||
1158 | g->finobjsur = g->finobj; /* all news are survivals */ | ||
1159 | |||
1160 | sweepgen(L, g, &g->tobefnz, NULL); | ||
1161 | |||
1162 | finishgencycle(L, g); | ||
1163 | correctgraylists(g); | ||
1164 | //printf("check: \n");lua_checkmemory(L); | ||
999 | } | 1165 | } |
1000 | 1166 | ||
1001 | 1167 | ||
1002 | static void entergen (lua_State *L, global_State *g) { | 1168 | static void entergen (lua_State *L, global_State *g) { |
1003 | lua_checkmemory(L); | 1169 | lua_assert(g->reallyold == NULL && g->old == NULL && g->survival == NULL); |
1004 | lua_assert(g->old == NULL && g->survival == NULL); | ||
1005 | luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ | 1170 | luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ |
1006 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | 1171 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ |
1007 | startgencycle(L, g); | 1172 | atomic(L); |
1008 | /* sweep all ellements making them old */ | 1173 | /* sweep all ellements making them old */ |
1009 | sweepgen(L, &g->allgc, g->survival, ~0, bitmask(OLDBIT)); | 1174 | sweep2old(L, &g->allgc); |
1010 | /* everything alive now is old; 'survival' is empty */ | 1175 | /* everything alive now is old */ |
1011 | g->old = g->survival = g->allgc; | 1176 | g->reallyold = g->old = g->survival = g->allgc; |
1012 | finishgencycle(L, g, bitmask(OLDBIT)); | 1177 | |
1013 | lua_checkmemory(L); | 1178 | /* repeat for 'finobj' lists */ |
1179 | sweep2old(L, &g->finobj); | ||
1180 | g->finobjrold = g->finobjold = g->finobjsur = g->finobj; | ||
1181 | |||
1182 | finishgencycle(L, g); | ||
1183 | correctgraylists(g); | ||
1184 | g->gckind = KGC_GEN; | ||
1185 | } | ||
1186 | |||
1187 | |||
1188 | static void enterinc (global_State *g) { | ||
1189 | makewhite(g, g->mainthread); | ||
1190 | whitelist(g, g->allgc); | ||
1191 | g->reallyold = g->old = g->survival = NULL; | ||
1192 | whitelist(g, g->finobj); | ||
1193 | g->finobjrold = g->finobjold = g->finobjsur = NULL; | ||
1194 | lua_assert(g->tobefnz == NULL); /* no need to sweep */ | ||
1195 | g->gcstate = GCSpause; | ||
1196 | g->gckind = KGC_NORMAL; | ||
1014 | } | 1197 | } |
1015 | 1198 | ||
1016 | 1199 | ||
@@ -1019,17 +1202,18 @@ void luaC_changemode (lua_State *L, int newmode) { | |||
1019 | if (newmode != g->gckind) { /* otherwise, nothing to be done */ | 1202 | if (newmode != g->gckind) { /* otherwise, nothing to be done */ |
1020 | if (newmode == KGC_GEN) /* entering generational mode? */ | 1203 | if (newmode == KGC_GEN) /* entering generational mode? */ |
1021 | entergen(L, g); | 1204 | entergen(L, g); |
1022 | else { /* entering incremental mode */ | 1205 | else |
1023 | lua_checkmemory(L); | 1206 | enterinc(g); /* entering incremental mode */ |
1024 | youngcollection(L, g); | ||
1025 | g->old = g->survival = NULL; | ||
1026 | lua_checkmemory(L); | ||
1027 | } | ||
1028 | g->gckind = newmode; | ||
1029 | } | 1207 | } |
1030 | } | 1208 | } |
1031 | 1209 | ||
1032 | 1210 | ||
1211 | static void fullgen (lua_State *L, global_State *g) { | ||
1212 | enterinc(g); | ||
1213 | entergen(L, g); | ||
1214 | } | ||
1215 | |||
1216 | |||
1033 | static void genstep (lua_State *L, global_State *g) { | 1217 | static void genstep (lua_State *L, global_State *g) { |
1034 | lu_mem mem; | 1218 | lu_mem mem; |
1035 | youngcollection(L, g); | 1219 | youngcollection(L, g); |
@@ -1094,10 +1278,10 @@ static void deletealllist (lua_State *L, GCObject *p) { | |||
1094 | 1278 | ||
1095 | void luaC_freeallobjects (lua_State *L) { | 1279 | void luaC_freeallobjects (lua_State *L) { |
1096 | global_State *g = G(L); | 1280 | global_State *g = G(L); |
1281 | luaC_changemode(L, KGC_NORMAL); | ||
1097 | separatetobefnz(g, 1); /* separate all objects with finalizers */ | 1282 | separatetobefnz(g, 1); /* separate all objects with finalizers */ |
1098 | lua_assert(g->finobj == NULL); | 1283 | lua_assert(g->finobj == NULL); |
1099 | callallpendingfinalizers(L); | 1284 | callallpendingfinalizers(L); |
1100 | lua_assert(g->tobefnz == NULL); | ||
1101 | deletealllist(L, g->finobj); | 1285 | deletealllist(L, g->finobj); |
1102 | deletealllist(L, g->allgc); | 1286 | deletealllist(L, g->allgc); |
1103 | deletealllist(L, g->fixedgc); /* collect fixed objects */ | 1287 | deletealllist(L, g->fixedgc); /* collect fixed objects */ |
@@ -1110,9 +1294,10 @@ static l_mem atomic (lua_State *L) { | |||
1110 | l_mem work; | 1294 | l_mem work; |
1111 | GCObject *origweak, *origall; | 1295 | GCObject *origweak, *origall; |
1112 | GCObject *grayagain = g->grayagain; /* save original list */ | 1296 | GCObject *grayagain = g->grayagain; /* save original list */ |
1297 | g->grayagain = NULL; | ||
1113 | lua_assert(g->ephemeron == NULL && g->weak == NULL); | 1298 | lua_assert(g->ephemeron == NULL && g->weak == NULL); |
1114 | lua_assert(!iswhite(g->mainthread)); | 1299 | lua_assert(!iswhite(g->mainthread)); |
1115 | g->gcstate = GCSinsideatomic; | 1300 | g->gcstate = GCSatomic; |
1116 | g->GCmemtrav = 0; /* start counting work */ | 1301 | g->GCmemtrav = 0; /* start counting work */ |
1117 | markobject(g, L); /* mark running thread */ | 1302 | markobject(g, L); /* mark running thread */ |
1118 | /* registry and global metatables may be changed by API */ | 1303 | /* registry and global metatables may be changed by API */ |
@@ -1123,7 +1308,6 @@ static l_mem atomic (lua_State *L) { | |||
1123 | propagateall(g); /* propagate changes */ | 1308 | propagateall(g); /* propagate changes */ |
1124 | work = g->GCmemtrav; /* stop counting (do not recount 'grayagain') */ | 1309 | work = g->GCmemtrav; /* stop counting (do not recount 'grayagain') */ |
1125 | g->gray = grayagain; | 1310 | g->gray = grayagain; |
1126 | g->grayagain = NULL; | ||
1127 | propagateall(g); /* traverse 'grayagain' list */ | 1311 | propagateall(g); /* traverse 'grayagain' list */ |
1128 | g->GCmemtrav = 0; /* restart counting */ | 1312 | g->GCmemtrav = 0; /* restart counting */ |
1129 | convergeephemerons(g); | 1313 | convergeephemerons(g); |
@@ -1141,13 +1325,14 @@ static l_mem atomic (lua_State *L) { | |||
1141 | convergeephemerons(g); | 1325 | convergeephemerons(g); |
1142 | /* at this point, all resurrected objects are marked. */ | 1326 | /* at this point, all resurrected objects are marked. */ |
1143 | /* remove dead objects from weak tables */ | 1327 | /* remove dead objects from weak tables */ |
1144 | clearkeys(g, g->ephemeron, NULL); /* clear keys from all ephemeron tables */ | 1328 | clearkeys(g, g->ephemeron); /* clear keys from all ephemeron tables */ |
1145 | clearkeys(g, g->allweak, NULL); /* clear keys from all 'allweak' tables */ | 1329 | clearkeys(g, g->allweak); /* clear keys from all 'allweak' tables */ |
1146 | /* clear values from resurrected weak tables */ | 1330 | /* clear values from resurrected weak tables */ |
1147 | clearvalues(g, g->weak, origweak); | 1331 | clearvalues(g, g->weak, origweak); |
1148 | clearvalues(g, g->allweak, origall); | 1332 | clearvalues(g, g->allweak, origall); |
1149 | luaS_clearcache(g); | 1333 | luaS_clearcache(g); |
1150 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ | 1334 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ |
1335 | lua_assert(g->gray == NULL); | ||
1151 | work += g->GCmemtrav; /* complete counting */ | 1336 | work += g->GCmemtrav; /* complete counting */ |
1152 | return work; /* estimate of memory marked by 'atomic' */ | 1337 | return work; /* estimate of memory marked by 'atomic' */ |
1153 | } | 1338 | } |
@@ -1181,12 +1366,12 @@ static lu_mem singlestep (lua_State *L) { | |||
1181 | case GCSpropagate: { | 1366 | case GCSpropagate: { |
1182 | g->GCmemtrav = 0; | 1367 | g->GCmemtrav = 0; |
1183 | if (g->gray == NULL) /* no more gray objects? */ | 1368 | if (g->gray == NULL) /* no more gray objects? */ |
1184 | g->gcstate = GCSatomic; /* finish propagate phase */ | 1369 | g->gcstate = GCSenteratomic; /* finish propagate phase */ |
1185 | else | 1370 | else |
1186 | propagatemark(g); /* traverse one gray object */ | 1371 | propagatemark(g); /* traverse one gray object */ |
1187 | return g->GCmemtrav; /* memory traversed in this step */ | 1372 | return g->GCmemtrav; /* memory traversed in this step */ |
1188 | } | 1373 | } |
1189 | case GCSatomic: { | 1374 | case GCSenteratomic: { |
1190 | lu_mem work; | 1375 | lu_mem work; |
1191 | propagateall(g); /* make sure gray list is empty */ | 1376 | propagateall(g); /* make sure gray list is empty */ |
1192 | work = atomic(L); /* work is what was traversed by 'atomic' */ | 1377 | work = atomic(L); /* work is what was traversed by 'atomic' */ |
@@ -1291,23 +1476,31 @@ void luaC_step (lua_State *L) { | |||
1291 | ** to sweep all objects to turn them back to white (as white has not | 1476 | ** to sweep all objects to turn them back to white (as white has not |
1292 | ** changed, nothing will be collected). | 1477 | ** changed, nothing will be collected). |
1293 | */ | 1478 | */ |
1294 | void luaC_fullgc (lua_State *L, int isemergency) { | 1479 | static void fullinc (lua_State *L, global_State *g) { |
1295 | global_State *g = G(L); | 1480 | if (keepinvariant(g)) /* black objects? */ |
1296 | lua_assert(g->gckind == KGC_NORMAL); | ||
1297 | if (isemergency) g->gckind = KGC_EMERGENCY; /* set flag */ | ||
1298 | if (keepinvariant(g)) { /* black objects? */ | ||
1299 | entersweep(L); /* sweep everything to turn them back to white */ | 1481 | entersweep(L); /* sweep everything to turn them back to white */ |
1300 | } | ||
1301 | /* finish any pending sweep phase to start a new cycle */ | 1482 | /* finish any pending sweep phase to start a new cycle */ |
1302 | luaC_runtilstate(L, bitmask(GCSpause)); | 1483 | luaC_runtilstate(L, bitmask(GCSpause)); |
1303 | luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ | 1484 | luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ |
1304 | /* estimate must be correct after a full GC cycle */ | 1485 | /* estimate must be correct after a full GC cycle */ |
1305 | lua_assert(g->GCestimate == gettotalbytes(g)); | 1486 | lua_assert(g->GCestimate == gettotalbytes(g)); |
1306 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ | 1487 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ |
1307 | g->gckind = KGC_NORMAL; | ||
1308 | setpause(g); | 1488 | setpause(g); |
1309 | } | 1489 | } |
1310 | 1490 | ||
1491 | |||
1492 | void luaC_fullgc (lua_State *L, int isemergency) { | ||
1493 | global_State *g = G(L); | ||
1494 | int gckind = g->gckind; | ||
1495 | if (isemergency) | ||
1496 | g->gckind = KGC_EMERGENCY; /* set flag */ | ||
1497 | if (gckind == KGC_NORMAL) | ||
1498 | fullinc(L, g); | ||
1499 | else | ||
1500 | fullgen(L, g); | ||
1501 | g->gckind = gckind; | ||
1502 | } | ||
1503 | |||
1311 | /* }====================================================== */ | 1504 | /* }====================================================== */ |
1312 | 1505 | ||
1313 | 1506 | ||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lgc.h,v 2.91 2015/12/21 13:02:14 roberto Exp roberto $ | 2 | ** $Id: lgc.h,v 2.92 2017/02/23 21:07:34 roberto Exp roberto $ |
3 | ** Garbage Collector | 3 | ** Garbage Collector |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -37,13 +37,14 @@ | |||
37 | ** Possible states of the Garbage Collector | 37 | ** Possible states of the Garbage Collector |
38 | */ | 38 | */ |
39 | #define GCSpropagate 0 | 39 | #define GCSpropagate 0 |
40 | #define GCSatomic 1 | 40 | #define GCSenteratomic 1 |
41 | #define GCSswpallgc 2 | 41 | #define GCSatomic 2 |
42 | #define GCSswpfinobj 3 | 42 | #define GCSswpallgc 3 |
43 | #define GCSswptobefnz 4 | 43 | #define GCSswpfinobj 4 |
44 | #define GCSswpend 5 | 44 | #define GCSswptobefnz 5 |
45 | #define GCScallfin 6 | 45 | #define GCSswpend 6 |
46 | #define GCSpause 7 | 46 | #define GCScallfin 7 |
47 | #define GCSpause 8 | ||
47 | 48 | ||
48 | 49 | ||
49 | #define issweepphase(g) \ | 50 | #define issweepphase(g) \ |
@@ -74,14 +75,17 @@ | |||
74 | #define testbit(x,b) testbits(x, bitmask(b)) | 75 | #define testbit(x,b) testbits(x, bitmask(b)) |
75 | 76 | ||
76 | 77 | ||
77 | /* Layout for bit use in 'marked' field: */ | 78 | /* |
78 | #define WHITE0BIT 0 /* object is white (type 0) */ | 79 | ** Layout for bit use in 'marked' field. First three bits are |
79 | #define WHITE1BIT 1 /* object is white (type 1) */ | 80 | ** used for object "age" in generational mode. |
80 | #define BLACKBIT 2 /* object is black */ | 81 | */ |
81 | #define FINALIZEDBIT 3 /* object has been marked for finalization */ | 82 | #define WHITE0BIT 3 /* object is white (type 0) */ |
82 | #define OLDBIT 4 /* object is old (gen. mode) */ | 83 | #define WHITE1BIT 4 /* object is white (type 1) */ |
84 | #define BLACKBIT 5 /* object is black */ | ||
85 | #define FINALIZEDBIT 6 /* object has been marked for finalization */ | ||
83 | #define TESTGRAYBIT 7 /* used by tests (luaL_checkmemory) */ | 86 | #define TESTGRAYBIT 7 /* used by tests (luaL_checkmemory) */ |
84 | 87 | ||
88 | |||
85 | #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) | 89 | #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) |
86 | 90 | ||
87 | 91 | ||
@@ -89,7 +93,6 @@ | |||
89 | #define isblack(x) testbit((x)->marked, BLACKBIT) | 93 | #define isblack(x) testbit((x)->marked, BLACKBIT) |
90 | #define isgray(x) /* neither white nor black */ \ | 94 | #define isgray(x) /* neither white nor black */ \ |
91 | (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) | 95 | (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) |
92 | #define isold(x) testbit((x)->marked, OLDBIT) | ||
93 | 96 | ||
94 | #define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) | 97 | #define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) |
95 | 98 | ||
@@ -103,6 +106,27 @@ | |||
103 | #define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) | 106 | #define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) |
104 | 107 | ||
105 | 108 | ||
109 | /* object age in generational mode */ | ||
110 | #define G_NEW 0 /* created in current cycle */ | ||
111 | #define G_SURVIVAL 1 /* created in previous cycle */ | ||
112 | #define G_OLD1 2 /* first full cycle as old */ | ||
113 | #define G_OLD0 3 /* marked old by frw. barrier in this cycle */ | ||
114 | #define G_OLD 4 /* really old object (not to be visited) */ | ||
115 | #define G_TOUCHED1 5 /* old object touched this cycle */ | ||
116 | #define G_TOUCHED2 6 /* old object touched in previous cycle */ | ||
117 | |||
118 | #define AGEBITS 7 /* all age bits (111) */ | ||
119 | |||
120 | #define getage(o) ((o)->marked & AGEBITS) | ||
121 | #define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a)) | ||
122 | #define isold(o) (getage(o) > G_SURVIVAL) | ||
123 | |||
124 | #define changeage(o,f,t) \ | ||
125 | check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t))) | ||
126 | |||
127 | #define ongraylist(o) (isgray(o) || getage(o) == G_TOUCHED2) | ||
128 | |||
129 | |||
106 | /* | 130 | /* |
107 | ** Does one step of collection when debt becomes positive. 'pre'/'pos' | 131 | ** Does one step of collection when debt becomes positive. 'pre'/'pos' |
108 | ** allows some adjustments to be done only when needed. macro | 132 | ** allows some adjustments to be done only when needed. macro |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lstate.c,v 2.133 2015/11/13 12:16:51 roberto Exp roberto $ | 2 | ** $Id: lstate.c,v 2.134 2017/02/23 21:07:34 roberto Exp roberto $ |
3 | ** Global State | 3 | ** Global State |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -319,7 +319,8 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { | |||
319 | g->gcstate = GCSpause; | 319 | g->gcstate = GCSpause; |
320 | g->gckind = KGC_NORMAL; | 320 | g->gckind = KGC_NORMAL; |
321 | g->allgc = g->finobj = g->tobefnz = g->fixedgc = NULL; | 321 | g->allgc = g->finobj = g->tobefnz = g->fixedgc = NULL; |
322 | g->survival = g->old = NULL; | 322 | g->survival = g->old = g->reallyold = NULL; |
323 | g->finobjsur = g->finobjold = g->finobjrold = NULL; | ||
323 | g->sweepgc = NULL; | 324 | g->sweepgc = NULL; |
324 | g->gray = g->grayagain = NULL; | 325 | g->gray = g->grayagain = NULL; |
325 | g->weak = g->ephemeron = g->allweak = NULL; | 326 | g->weak = g->ephemeron = g->allweak = NULL; |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lstate.h,v 2.134 2017/02/15 18:52:13 roberto Exp roberto $ | 2 | ** $Id: lstate.h,v 2.135 2017/02/23 21:07:34 roberto Exp $ |
3 | ** Global State | 3 | ** Global State |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -160,8 +160,12 @@ typedef struct global_State { | |||
160 | GCObject *tobefnz; /* list of userdata to be GC */ | 160 | GCObject *tobefnz; /* list of userdata to be GC */ |
161 | GCObject *fixedgc; /* list of objects not to be collected */ | 161 | GCObject *fixedgc; /* list of objects not to be collected */ |
162 | /* fields for generational collector */ | 162 | /* fields for generational collector */ |
163 | GCObject *old; /* start of old objects */ | ||
164 | GCObject *survival; /* start of objects that survived one GC cycle */ | 163 | GCObject *survival; /* start of objects that survived one GC cycle */ |
164 | GCObject *old; /* start of old objects */ | ||
165 | GCObject *reallyold; /* old objects with more than one cycle */ | ||
166 | GCObject *finobjsur; /* list of survival objects with finalizers */ | ||
167 | GCObject *finobjold; /* list of old objects with finalizers */ | ||
168 | GCObject *finobjrold; /* list of really old objects with finalizers */ | ||
165 | struct lua_State *twups; /* list of threads with open upvalues */ | 169 | struct lua_State *twups; /* list of threads with open upvalues */ |
166 | unsigned int gcfinnum; /* number of finalizers to call in each GC step */ | 170 | unsigned int gcfinnum; /* number of finalizers to call in each GC step */ |
167 | int gcpause; /* size of pause between successive GCs */ | 171 | int gcpause; /* size of pause between successive GCs */ |