diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2023-11-29 16:22:09 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2023-11-29 16:22:09 -0300 |
| commit | 63d68bd657b7386c9c58b4439a100ea0ccbd633e (patch) | |
| tree | 117ae53a7b1cbfd242370af0c5250fca85fd2a7d | |
| parent | 011850a8f86f514d1ba2ebf7a9411c8036b917f4 (diff) | |
| download | lua-63d68bd657b7386c9c58b4439a100ea0ccbd633e.tar.gz lua-63d68bd657b7386c9c58b4439a100ea0ccbd633e.tar.bz2 lua-63d68bd657b7386c9c58b4439a100ea0ccbd633e.zip | |
Comments detailing the ages for generational GC
Plus other comments and small details.
| -rw-r--r-- | lauxlib.c | 5 | ||||
| -rw-r--r-- | lgc.c | 11 | ||||
| -rw-r--r-- | lgc.h | 44 | ||||
| -rw-r--r-- | lstate.c | 5 | ||||
| -rw-r--r-- | ltests.c | 10 |
5 files changed, 59 insertions, 16 deletions
| @@ -1139,12 +1139,11 @@ static unsigned int luai_makeseed (void) { | |||
| 1139 | unsigned int res; | 1139 | unsigned int res; |
| 1140 | unsigned int i; | 1140 | unsigned int i; |
| 1141 | time_t t = time(NULL); | 1141 | time_t t = time(NULL); |
| 1142 | void *h = buff; | ||
| 1143 | char *b = (char*)buff; | 1142 | char *b = (char*)buff; |
| 1144 | addbuff(b, h); /* local variable's address */ | 1143 | addbuff(b, b); /* local variable's address */ |
| 1145 | addbuff(b, t); /* time */ | 1144 | addbuff(b, t); /* time */ |
| 1146 | /* fill (rare but possible) remain of the buffer with zeros */ | 1145 | /* fill (rare but possible) remain of the buffer with zeros */ |
| 1147 | memset(b, 0, BUFSEED * sizeof(int) - BUFSEEDB); | 1146 | memset(b, 0, sizeof(buff) - BUFSEEDB); |
| 1148 | res = buff[0]; | 1147 | res = buff[0]; |
| 1149 | for (i = 0; i < BUFSEED; i++) | 1148 | for (i = 0; i < BUFSEED; i++) |
| 1150 | res ^= (res >> 3) + (res << 7) + buff[i]; | 1149 | res ^= (res >> 3) + (res << 7) + buff[i]; |
| @@ -1121,6 +1121,7 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, | |||
| 1121 | curr->marked = cast_byte(marked | G_SURVIVAL | white); | 1121 | curr->marked = cast_byte(marked | G_SURVIVAL | white); |
| 1122 | } | 1122 | } |
| 1123 | else { /* all other objects will be old, and so keep their color */ | 1123 | else { /* all other objects will be old, and so keep their color */ |
| 1124 | lua_assert(getage(curr) != G_OLD1); /* advanced in 'markold' */ | ||
| 1124 | setage(curr, nextage[getage(curr)]); | 1125 | setage(curr, nextage[getage(curr)]); |
| 1125 | if (getage(curr) == G_OLD1 && *pfirstold1 == NULL) | 1126 | if (getage(curr) == G_OLD1 && *pfirstold1 == NULL) |
| 1126 | *pfirstold1 = curr; /* first OLD1 object in the list */ | 1127 | *pfirstold1 = curr; /* first OLD1 object in the list */ |
| @@ -1145,13 +1146,15 @@ static void whitelist (global_State *g, GCObject *p) { | |||
| 1145 | 1146 | ||
| 1146 | 1147 | ||
| 1147 | /* | 1148 | /* |
| 1148 | ** Correct a list of gray objects. Return pointer to where rest of the | 1149 | ** Correct a list of gray objects. Return a pointer to the last element |
| 1149 | ** list should be linked. | 1150 | ** left on the list, so that we can link another list to the end of |
| 1151 | ** this one. | ||
| 1150 | ** Because this correction is done after sweeping, young objects might | 1152 | ** Because this correction is done after sweeping, young objects might |
| 1151 | ** be turned white and still be in the list. They are only removed. | 1153 | ** be turned white and still be in the list. They are only removed. |
| 1152 | ** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list; | 1154 | ** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list; |
| 1153 | ** Non-white threads also remain on the list; 'TOUCHED2' objects become | 1155 | ** Non-white threads also remain on the list. 'TOUCHED2' objects and |
| 1154 | ** regular old; they and anything else are removed from the list. | 1156 | ** anything else become regular old, are marked black, and are removed |
| 1157 | ** from the list. | ||
| 1155 | */ | 1158 | */ |
| 1156 | static GCObject **correctgraylist (GCObject **p) { | 1159 | static GCObject **correctgraylist (GCObject **p) { |
| 1157 | GCObject *curr; | 1160 | GCObject *curr; |
| @@ -123,6 +123,43 @@ | |||
| 123 | #define changeage(o,f,t) \ | 123 | #define changeage(o,f,t) \ |
| 124 | check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t))) | 124 | check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t))) |
| 125 | 125 | ||
| 126 | /* | ||
| 127 | ** In generational mode, objects are created 'new'. After surviving one | ||
| 128 | ** cycle, they become 'survival'. Both 'new' and 'survival' can point | ||
| 129 | ** to any other object, as they are traversed at the end of the cycle. | ||
| 130 | ** We call them both 'young' objects. | ||
| 131 | ** If a survival object survives another cycle, it becomes 'old1'. | ||
| 132 | ** 'old1' objects can still point to survival objects (but not to | ||
| 133 | ** new objects), so they still must be traversed. After another cycle | ||
| 134 | ** (that, being old, 'old1' objects will "survive" no matter what) | ||
| 135 | ** finally the 'old1' object becomes really 'old', and then they | ||
| 136 | ** are no more traversed. | ||
| 137 | ** | ||
| 138 | ** To keep its invariants, the generational mode uses the same barriers | ||
| 139 | ** also used by the incremental mode. If a young object is caught in a | ||
| 140 | ** foward barrier, it cannot become old immediately, because it can | ||
| 141 | ** still point to other young objects. Instead, it becomes 'old0', | ||
| 142 | ** which in the next cycle becomes 'old1'. So, 'old0' objects is | ||
| 143 | ** old but can point to new and survival objects; 'old1' is old | ||
| 144 | ** but cannot point to new objects; and 'old' cannot point to any | ||
| 145 | ** young object. | ||
| 146 | ** | ||
| 147 | ** If any old object ('old0', 'old1', 'old') is caught in a back | ||
| 148 | ** barrier, it becomes 'touched1' and goes into a gray list, to be | ||
| 149 | ** visited at the end of the cycle. There it evolves to 'touched2', | ||
| 150 | ** which can point to survivals but not to new objects. In yet another | ||
| 151 | ** cycle then it becomes 'old' again. | ||
| 152 | ** | ||
| 153 | ** The generational mode must also control the colors of objects, | ||
| 154 | ** because of the barriers. While the mutator is running, young objects | ||
| 155 | ** are kept white. 'old', 'old1', and 'touched2' objects are kept black, | ||
| 156 | ** as they cannot point to new objects; exceptions are threads and open | ||
| 157 | ** upvalues, which age to 'old1' and 'old' but are kept gray. 'old0' | ||
| 158 | ** objects may be gray or black, as in the incremental mode. 'touched1' | ||
| 159 | ** objects are kept gray, as they must be visited again at the end of | ||
| 160 | ** the cycle. | ||
| 161 | */ | ||
| 162 | |||
| 126 | 163 | ||
| 127 | /* Default Values for GC parameters */ | 164 | /* Default Values for GC parameters */ |
| 128 | 165 | ||
| @@ -161,9 +198,10 @@ | |||
| 161 | ** (value * original parameter / 100). | 198 | ** (value * original parameter / 100). |
| 162 | ** | 199 | ** |
| 163 | ** For most parameters, which are typically larger than 100%, 2^n is | 200 | ** For most parameters, which are typically larger than 100%, 2^n is |
| 164 | ** 16 (2^4), allowing maximum values up to 1599. For the minor | 201 | ** 16 (2^4), allowing maximum values up to ~1500%, with a granularity |
| 165 | ** multiplier, which is typically smaller, 2^n is 64 (2^6) to allow more | 202 | ** of ~6%. For the minor multiplier, which is typically smaller, |
| 166 | ** precision. | 203 | ** 2^n is 64 (2^6) to allow more precision. In that case, the maximum |
| 204 | ** value is ~400%, with a granularity of ~1.5%. | ||
| 167 | */ | 205 | */ |
| 168 | #define gcparamshift(p) \ | 206 | #define gcparamshift(p) \ |
| 169 | (offsetof(global_State, p) == offsetof(global_State, genminormul) ? 6 : 4) | 207 | (offsetof(global_State, p) == offsetof(global_State, genminormul) ? 6 : 4) |
| @@ -52,8 +52,9 @@ typedef struct LG { | |||
| 52 | 52 | ||
| 53 | 53 | ||
| 54 | /* | 54 | /* |
| 55 | ** set GCdebt to a new value keeping the value (totalobjs + GCdebt) | 55 | ** set GCdebt to a new value keeping the real number of allocated |
| 56 | ** invariant (and avoiding underflows in 'totalobjs') | 56 | ** objects (totalobjs - GCdebt) invariant and avoiding overflows in |
| 57 | ** 'totalobjs'. | ||
| 57 | */ | 58 | */ |
| 58 | void luaE_setdebt (global_State *g, l_obj debt) { | 59 | void luaE_setdebt (global_State *g, l_obj debt) { |
| 59 | l_obj tb = gettotalobjs(g); | 60 | l_obj tb = gettotalobjs(g); |
| @@ -302,8 +302,8 @@ static int testobjref1 (global_State *g, GCObject *f, GCObject *t) { | |||
| 302 | else { /* generational mode */ | 302 | else { /* generational mode */ |
| 303 | if ((getage(f) == G_OLD && isblack(f)) && !isold(t)) | 303 | if ((getage(f) == G_OLD && isblack(f)) && !isold(t)) |
| 304 | return 0; | 304 | return 0; |
| 305 | if (((getage(f) == G_OLD1 || getage(f) == G_TOUCHED2) && isblack(f)) && | 305 | if ((getage(f) == G_OLD1 || getage(f) == G_TOUCHED2) && |
| 306 | getage(t) == G_NEW) | 306 | getage(t) == G_NEW) |
| 307 | return 0; | 307 | return 0; |
| 308 | return 1; | 308 | return 1; |
| 309 | } | 309 | } |
| @@ -510,7 +510,8 @@ static void checkrefs (global_State *g, GCObject *o) { | |||
| 510 | ** * objects must be old enough for their lists ('listage'). | 510 | ** * objects must be old enough for their lists ('listage'). |
| 511 | ** * old objects cannot be white. | 511 | ** * old objects cannot be white. |
| 512 | ** * old objects must be black, except for 'touched1', 'old0', | 512 | ** * old objects must be black, except for 'touched1', 'old0', |
| 513 | ** threads, and open upvalues. | 513 | ** threads, and open upvalues. |
| 514 | ** * 'touched1' objects must be gray. | ||
| 514 | */ | 515 | */ |
| 515 | static void checkobject (global_State *g, GCObject *o, int maybedead, | 516 | static void checkobject (global_State *g, GCObject *o, int maybedead, |
| 516 | int listage) { | 517 | int listage) { |
| @@ -520,14 +521,15 @@ static void checkobject (global_State *g, GCObject *o, int maybedead, | |||
| 520 | assert(g->gcstate != GCSpause || iswhite(o)); | 521 | assert(g->gcstate != GCSpause || iswhite(o)); |
| 521 | if (g->gckind == KGC_GEN) { /* generational mode? */ | 522 | if (g->gckind == KGC_GEN) { /* generational mode? */ |
| 522 | assert(getage(o) >= listage); | 523 | assert(getage(o) >= listage); |
| 523 | assert(!iswhite(o) || !isold(o)); | ||
| 524 | if (isold(o)) { | 524 | if (isold(o)) { |
| 525 | assert(!iswhite(o)); | ||
| 525 | assert(isblack(o) || | 526 | assert(isblack(o) || |
| 526 | getage(o) == G_TOUCHED1 || | 527 | getage(o) == G_TOUCHED1 || |
| 527 | getage(o) == G_OLD0 || | 528 | getage(o) == G_OLD0 || |
| 528 | o->tt == LUA_VTHREAD || | 529 | o->tt == LUA_VTHREAD || |
| 529 | (o->tt == LUA_VUPVAL && upisopen(gco2upv(o)))); | 530 | (o->tt == LUA_VUPVAL && upisopen(gco2upv(o)))); |
| 530 | } | 531 | } |
| 532 | assert(getage(o) != G_TOUCHED1 || isgray(o)); | ||
| 531 | } | 533 | } |
| 532 | checkrefs(g, o); | 534 | checkrefs(g, o); |
| 533 | } | 535 | } |
