diff options
| -rw-r--r-- | lgc.c | 94 |
1 files changed, 50 insertions, 44 deletions
| @@ -75,6 +75,11 @@ | |||
| 75 | #define set2gray(x) resetbits(x->marked, maskcolors) | 75 | #define set2gray(x) resetbits(x->marked, maskcolors) |
| 76 | 76 | ||
| 77 | 77 | ||
| 78 | /* make an object black (coming from any color) */ | ||
| 79 | #define set2black(x) \ | ||
| 80 | (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT))) | ||
| 81 | |||
| 82 | |||
| 78 | #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) | 83 | #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) |
| 79 | 84 | ||
| 80 | #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) | 85 | #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) |
| @@ -135,15 +140,23 @@ static GCObject **getgclist (GCObject *o) { | |||
| 135 | 140 | ||
| 136 | 141 | ||
| 137 | /* | 142 | /* |
| 138 | ** Link a collectable object 'o' with a known type into list pointed by 'p'. | 143 | ** Link a collectable object 'o' with a known type into the list 'p'. |
| 144 | ** (Must be a macro to access the 'gclist' field in different types.) | ||
| 139 | */ | 145 | */ |
| 140 | #define linkgclist(o,p) ((o)->gclist = (p), (p) = obj2gco(o)) | 146 | #define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p)) |
| 147 | |||
| 148 | static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) { | ||
| 149 | lua_assert(!isgray(o)); /* cannot be in a gray list */ | ||
| 150 | *pnext = *list; | ||
| 151 | *list = o; | ||
| 152 | set2gray(o); /* now it is */ | ||
| 153 | } | ||
| 141 | 154 | ||
| 142 | 155 | ||
| 143 | /* | 156 | /* |
| 144 | ** Link a generic collectable object 'o' into list pointed by 'p'. | 157 | ** Link a generic collectable object 'o' into the list 'p'. |
| 145 | */ | 158 | */ |
| 146 | #define linkobjgclist(o,p) (*getgclist(o) = (p), (p) = obj2gco(o)) | 159 | #define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p)) |
| 147 | 160 | ||
| 148 | 161 | ||
| 149 | 162 | ||
| @@ -219,9 +232,10 @@ void luaC_barrierback_ (lua_State *L, GCObject *o) { | |||
| 219 | global_State *g = G(L); | 232 | global_State *g = G(L); |
| 220 | lua_assert(isblack(o) && !isdead(g, o)); | 233 | lua_assert(isblack(o) && !isdead(g, o)); |
| 221 | lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1)); | 234 | lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1)); |
| 222 | if (getage(o) != G_TOUCHED2) /* not already in gray list? */ | 235 | if (getage(o) == G_TOUCHED2) /* already in gray list? */ |
| 223 | linkobjgclist(o, g->grayagain); /* link it in 'grayagain' */ | 236 | set2gray(o); /* make it gray to become touched1 */ |
| 224 | set2gray(o); /* make object gray (again) */ | 237 | else /* link it in 'grayagain' and paint it gray */ |
| 238 | linkobjgclist(o, g->grayagain); | ||
| 225 | if (isold(o)) /* generational mode? */ | 239 | if (isold(o)) /* generational mode? */ |
| 226 | setage(o, G_TOUCHED1); /* touched in current cycle */ | 240 | setage(o, G_TOUCHED1); /* touched in current cycle */ |
| 227 | } | 241 | } |
| @@ -264,25 +278,30 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { | |||
| 264 | 278 | ||
| 265 | 279 | ||
| 266 | /* | 280 | /* |
| 267 | ** Mark an object. Userdata, strings, and closed upvalues are visited | 281 | ** Mark an object. Userdata with no user values, strings, and closed |
| 268 | ** and turned black here. Other objects are marked gray and added | 282 | ** upvalues are visited and turned black here. Open upvalues are |
| 269 | ** to appropriate list to be visited (and turned black) later. (Open | 283 | ** already indirectly linked through their respective threads in the |
| 270 | ** upvalues are already indirectly linked through the 'twups' list. They | 284 | ** 'twups' list, so they don't go to the gray list; nevertheless, they |
| 271 | ** are kept gray to avoid barriers, as their values will be revisited by | 285 | ** are kept gray to avoid barriers, as their values will be revisited |
| 272 | ** the thread or by 'remarkupvals'.) | 286 | ** by the thread or by 'remarkupvals'. Other objects are added to the |
| 287 | ** gray list to be visited (and turned black) later. Both userdata and | ||
| 288 | ** upvalues can call this function recursively, but this recursion goes | ||
| 289 | ** for at most two levels: An upvalue cannot refer to another upvalue | ||
| 290 | ** (only closures can), and a userdata's metatable must be a table. | ||
| 273 | */ | 291 | */ |
| 274 | static void reallymarkobject (global_State *g, GCObject *o) { | 292 | static void reallymarkobject (global_State *g, GCObject *o) { |
| 275 | set2gray(o); | ||
| 276 | switch (o->tt) { | 293 | switch (o->tt) { |
| 277 | case LUA_VSHRSTR: | 294 | case LUA_VSHRSTR: |
| 278 | case LUA_VLNGSTR: { | 295 | case LUA_VLNGSTR: { |
| 279 | nw2black(o); /* nothing to visit */ | 296 | set2black(o); /* nothing to visit */ |
| 280 | break; | 297 | break; |
| 281 | } | 298 | } |
| 282 | case LUA_VUPVAL: { | 299 | case LUA_VUPVAL: { |
| 283 | UpVal *uv = gco2upv(o); | 300 | UpVal *uv = gco2upv(o); |
| 284 | if (!upisopen(uv)) /* open upvalues are kept gray */ | 301 | if (upisopen(uv)) |
| 285 | nw2black(o); /* closed upvalues are visited here */ | 302 | set2gray(uv); /* open upvalues are kept gray */ |
| 303 | else | ||
| 304 | set2black(o); /* closed upvalues are visited here */ | ||
| 286 | markvalue(g, uv->v); /* mark its content */ | 305 | markvalue(g, uv->v); /* mark its content */ |
| 287 | break; | 306 | break; |
| 288 | } | 307 | } |
| @@ -290,7 +309,7 @@ static void reallymarkobject (global_State *g, GCObject *o) { | |||
| 290 | Udata *u = gco2u(o); | 309 | Udata *u = gco2u(o); |
| 291 | if (u->nuvalue == 0) { /* no user values? */ | 310 | if (u->nuvalue == 0) { /* no user values? */ |
| 292 | markobjectN(g, u->metatable); /* mark its metatable */ | 311 | markobjectN(g, u->metatable); /* mark its metatable */ |
| 293 | nw2black(o); /* nothing else to mark */ | 312 | set2black(o); /* nothing else to mark */ |
| 294 | break; | 313 | break; |
| 295 | } | 314 | } |
| 296 | /* else... */ | 315 | /* else... */ |
| @@ -402,17 +421,11 @@ static void restartcollection (global_State *g) { | |||
| 402 | ** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go | 421 | ** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go |
| 403 | ** back to a gray list, but then it must become OLD. (That is what | 422 | ** back to a gray list, but then it must become OLD. (That is what |
| 404 | ** 'correctgraylist' does when it finds a TOUCHED2 object.) | 423 | ** 'correctgraylist' does when it finds a TOUCHED2 object.) |
| 405 | ** It is defined as a macro because 'gclist' is not a unique field in | ||
| 406 | ** different collectable objects. | ||
| 407 | */ | 424 | */ |
| 408 | #define genlink(g,o) genlink_(g, obj2gco(o), &(o)->gclist) | 425 | static void genlink (global_State *g, GCObject *o) { |
| 409 | |||
| 410 | static void genlink_ (global_State *g, GCObject *o, GCObject **pnext) { | ||
| 411 | lua_assert(isblack(o)); | 426 | lua_assert(isblack(o)); |
| 412 | if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */ | 427 | if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */ |
| 413 | *pnext = g->grayagain; /* link it back in 'grayagain' */ | 428 | linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */ |
| 414 | g->grayagain = o; | ||
| 415 | set2gray(o); | ||
| 416 | } /* everything else do not need to be linked back */ | 429 | } /* everything else do not need to be linked back */ |
| 417 | else if (getage(o) == G_TOUCHED2) | 430 | else if (getage(o) == G_TOUCHED2) |
| 418 | changeage(o, G_TOUCHED2, G_OLD); /* advance age */ | 431 | changeage(o, G_TOUCHED2, G_OLD); /* advance age */ |
| @@ -496,10 +509,8 @@ static int traverseephemeron (global_State *g, Table *h, int inv) { | |||
| 496 | linkgclist(h, g->ephemeron); /* have to propagate again */ | 509 | linkgclist(h, g->ephemeron); /* have to propagate again */ |
| 497 | else if (hasclears) /* table has white keys? */ | 510 | else if (hasclears) /* table has white keys? */ |
| 498 | linkgclist(h, g->allweak); /* may have to clean white keys */ | 511 | linkgclist(h, g->allweak); /* may have to clean white keys */ |
| 499 | else { | 512 | else |
| 500 | nw2black(h); /* 'genlink' expects black objects */ | 513 | genlink(g, obj2gco(h)); /* check whether collector still needs to see it */ |
| 501 | genlink(g, h); /* check whether collector still needs to see it */ | ||
| 502 | } | ||
| 503 | return marked; | 514 | return marked; |
| 504 | } | 515 | } |
| 505 | 516 | ||
| @@ -519,7 +530,7 @@ static void traversestrongtable (global_State *g, Table *h) { | |||
| 519 | markvalue(g, gval(n)); | 530 | markvalue(g, gval(n)); |
| 520 | } | 531 | } |
| 521 | } | 532 | } |
| 522 | genlink(g, h); | 533 | genlink(g, obj2gco(h)); |
| 523 | } | 534 | } |
| 524 | 535 | ||
| 525 | 536 | ||
| @@ -531,7 +542,6 @@ static lu_mem traversetable (global_State *g, Table *h) { | |||
| 531 | (cast_void(weakkey = strchr(svalue(mode), 'k')), | 542 | (cast_void(weakkey = strchr(svalue(mode), 'k')), |
| 532 | cast_void(weakvalue = strchr(svalue(mode), 'v')), | 543 | cast_void(weakvalue = strchr(svalue(mode), 'v')), |
| 533 | (weakkey || weakvalue))) { /* is really weak? */ | 544 | (weakkey || weakvalue))) { /* is really weak? */ |
| 534 | set2gray(h); /* turn it back to gray, as it probably goes to a list */ | ||
| 535 | if (!weakkey) /* strong keys? */ | 545 | if (!weakkey) /* strong keys? */ |
| 536 | traverseweakvalue(g, h); | 546 | traverseweakvalue(g, h); |
| 537 | else if (!weakvalue) /* strong values? */ | 547 | else if (!weakvalue) /* strong values? */ |
| @@ -550,7 +560,7 @@ static int traverseudata (global_State *g, Udata *u) { | |||
| 550 | markobjectN(g, u->metatable); /* mark its metatable */ | 560 | markobjectN(g, u->metatable); /* mark its metatable */ |
| 551 | for (i = 0; i < u->nuvalue; i++) | 561 | for (i = 0; i < u->nuvalue; i++) |
| 552 | markvalue(g, &u->uv[i].uv); | 562 | markvalue(g, &u->uv[i].uv); |
| 553 | genlink(g, u); | 563 | genlink(g, obj2gco(u)); |
| 554 | return 1 + u->nuvalue; | 564 | return 1 + u->nuvalue; |
| 555 | } | 565 | } |
| 556 | 566 | ||
| @@ -612,10 +622,8 @@ static int traverseLclosure (global_State *g, LClosure *cl) { | |||
| 612 | static int traversethread (global_State *g, lua_State *th) { | 622 | static int traversethread (global_State *g, lua_State *th) { |
| 613 | UpVal *uv; | 623 | UpVal *uv; |
| 614 | StkId o = th->stack; | 624 | StkId o = th->stack; |
| 615 | if (isold(th) || g->gcstate == GCSpropagate) { | 625 | if (isold(th) || g->gcstate == GCSpropagate) |
| 616 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ | 626 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ |
| 617 | set2gray(th); | ||
| 618 | } | ||
| 619 | if (o == NULL) | 627 | if (o == NULL) |
| 620 | return 1; /* stack not completely built yet */ | 628 | return 1; /* stack not completely built yet */ |
| 621 | lua_assert(g->gcstate == GCSatomic || | 629 | lua_assert(g->gcstate == GCSatomic || |
| @@ -641,8 +649,7 @@ static int traversethread (global_State *g, lua_State *th) { | |||
| 641 | 649 | ||
| 642 | 650 | ||
| 643 | /* | 651 | /* |
| 644 | ** traverse one gray object, turning it to black (except for threads, | 652 | ** traverse one gray object, turning it to black. |
| 645 | ** which are always gray). | ||
| 646 | */ | 653 | */ |
| 647 | static lu_mem propagatemark (global_State *g) { | 654 | static lu_mem propagatemark (global_State *g) { |
| 648 | GCObject *o = g->gray; | 655 | GCObject *o = g->gray; |
| @@ -684,8 +691,10 @@ static void convergeephemerons (global_State *g) { | |||
| 684 | g->ephemeron = NULL; /* tables may return to this list when traversed */ | 691 | g->ephemeron = NULL; /* tables may return to this list when traversed */ |
| 685 | changed = 0; | 692 | changed = 0; |
| 686 | while ((w = next) != NULL) { /* for each ephemeron table */ | 693 | while ((w = next) != NULL) { /* for each ephemeron table */ |
| 687 | next = gco2t(w)->gclist; /* list is rebuilt during loop */ | 694 | Table *h = gco2t(w); |
| 688 | if (traverseephemeron(g, gco2t(w), dir)) { /* marked some value? */ | 695 | next = h->gclist; /* list is rebuilt during loop */ |
| 696 | nw2black(h); /* out of the list (for now) */ | ||
| 697 | if (traverseephemeron(g, h, dir)) { /* marked some value? */ | ||
| 689 | propagateall(g); /* propagate changes */ | 698 | propagateall(g); /* propagate changes */ |
| 690 | changed = 1; /* will have to revisit all ephemeron tables */ | 699 | changed = 1; /* will have to revisit all ephemeron tables */ |
| 691 | } | 700 | } |
| @@ -1048,7 +1057,6 @@ static void sweep2old (lua_State *L, GCObject **p) { | |||
| 1048 | if (curr->tt == LUA_VTHREAD) { /* threads must be watched */ | 1057 | if (curr->tt == LUA_VTHREAD) { /* threads must be watched */ |
| 1049 | lua_State *th = gco2th(curr); | 1058 | lua_State *th = gco2th(curr); |
| 1050 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ | 1059 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ |
| 1051 | set2gray(th); | ||
| 1052 | } | 1060 | } |
| 1053 | else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr))) | 1061 | else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr))) |
| 1054 | set2gray(curr); /* open upvalues are always gray */ | 1062 | set2gray(curr); /* open upvalues are always gray */ |
| @@ -1183,10 +1191,8 @@ static void markold (global_State *g, GCObject *from, GCObject *to) { | |||
| 1183 | if (getage(p) == G_OLD1) { | 1191 | if (getage(p) == G_OLD1) { |
| 1184 | lua_assert(!iswhite(p)); | 1192 | lua_assert(!iswhite(p)); |
| 1185 | changeage(p, G_OLD1, G_OLD); /* now they are old */ | 1193 | changeage(p, G_OLD1, G_OLD); /* now they are old */ |
| 1186 | if (isblack(p)) { | 1194 | if (isblack(p)) |
| 1187 | set2gray(p); /* should be '2white', but gray works too */ | ||
| 1188 | reallymarkobject(g, p); | 1195 | reallymarkobject(g, p); |
| 1189 | } | ||
| 1190 | } | 1196 | } |
| 1191 | } | 1197 | } |
| 1192 | } | 1198 | } |
