diff options
| -rw-r--r-- | lapi.c | 9 | ||||
| -rw-r--r-- | lgc.c | 253 | ||||
| -rw-r--r-- | lgc.h | 6 | ||||
| -rw-r--r-- | llimits.h | 2 | ||||
| -rw-r--r-- | lmem.c | 8 | ||||
| -rw-r--r-- | lstate.c | 15 | ||||
| -rw-r--r-- | lstate.h | 10 | ||||
| -rw-r--r-- | ltests.c | 11 | ||||
| -rw-r--r-- | lua.c | 4 |
9 files changed, 162 insertions, 156 deletions
| @@ -1154,11 +1154,11 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { | |||
| 1154 | } | 1154 | } |
| 1155 | case LUA_GCCOUNT: { | 1155 | case LUA_GCCOUNT: { |
| 1156 | /* GC values are expressed in Kbytes: #bytes/2^10 */ | 1156 | /* GC values are expressed in Kbytes: #bytes/2^10 */ |
| 1157 | res = cast_int(gettotalbytes(g) >> 10); | 1157 | res = cast_int(g->totalbytes >> 10); |
| 1158 | break; | 1158 | break; |
| 1159 | } | 1159 | } |
| 1160 | case LUA_GCCOUNTB: { | 1160 | case LUA_GCCOUNTB: { |
| 1161 | res = cast_int(gettotalbytes(g) & 0x3ff); | 1161 | res = cast_int(g->totalbytes & 0x3ff); |
| 1162 | break; | 1162 | break; |
| 1163 | } | 1163 | } |
| 1164 | case LUA_GCSTEP: { | 1164 | case LUA_GCSTEP: { |
| @@ -1171,7 +1171,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { | |||
| 1171 | luaC_step(L); | 1171 | luaC_step(L); |
| 1172 | } | 1172 | } |
| 1173 | else { /* add 'data' to total debt */ | 1173 | else { /* add 'data' to total debt */ |
| 1174 | debt = cast(l_mem, data) * 1024 + g->GCdebt; | 1174 | debt = data + g->GCdebt; |
| 1175 | luaE_setdebt(g, debt); | 1175 | luaE_setdebt(g, debt); |
| 1176 | luaC_checkGC(L); | 1176 | luaC_checkGC(L); |
| 1177 | } | 1177 | } |
| @@ -1217,7 +1217,8 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { | |||
| 1217 | if (stepmul != 0) | 1217 | if (stepmul != 0) |
| 1218 | setgcparam(g->gcstepmul, stepmul); | 1218 | setgcparam(g->gcstepmul, stepmul); |
| 1219 | if (stepsize != 0) | 1219 | if (stepsize != 0) |
| 1220 | g->gcstepsize = stepsize; | 1220 | g->gcstepsize = (stepsize <= log2maxs(l_mem)) ? stepsize |
| 1221 | : log2maxs(l_mem); | ||
| 1221 | luaC_changemode(L, KGC_INC); | 1222 | luaC_changemode(L, KGC_INC); |
| 1222 | break; | 1223 | break; |
| 1223 | } | 1224 | } |
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "ldo.h" | 19 | #include "ldo.h" |
| 20 | #include "lfunc.h" | 20 | #include "lfunc.h" |
| 21 | #include "lgc.h" | 21 | #include "lgc.h" |
| 22 | #include "llex.h" | ||
| 22 | #include "lmem.h" | 23 | #include "lmem.h" |
| 23 | #include "lobject.h" | 24 | #include "lobject.h" |
| 24 | #include "lstate.h" | 25 | #include "lstate.h" |
| @@ -32,7 +33,7 @@ | |||
| 32 | ** (Large enough to dissipate fixed overheads but small enough | 33 | ** (Large enough to dissipate fixed overheads but small enough |
| 33 | ** to allow small steps for the collector.) | 34 | ** to allow small steps for the collector.) |
| 34 | */ | 35 | */ |
| 35 | #define GCSWEEPMAX 100 | 36 | #define GCSWEEPMAX 20 |
| 36 | 37 | ||
| 37 | /* | 38 | /* |
| 38 | ** Maximum number of finalizers to call in each single step. | 39 | ** Maximum number of finalizers to call in each single step. |
| @@ -46,19 +47,6 @@ | |||
| 46 | #define GCFINALIZECOST 50 | 47 | #define GCFINALIZECOST 50 |
| 47 | 48 | ||
| 48 | 49 | ||
| 49 | /* | ||
| 50 | ** The equivalent, in bytes, of one unit of "work" (visiting a slot, | ||
| 51 | ** sweeping an object, etc.) | ||
| 52 | */ | ||
| 53 | #define WORK2MEM sizeof(TValue) | ||
| 54 | |||
| 55 | |||
| 56 | /* | ||
| 57 | ** macro to adjust 'pause': 'pause' is actually used like | ||
| 58 | ** 'pause / PAUSEADJ' (value chosen by tests) | ||
| 59 | */ | ||
| 60 | #define PAUSEADJ 100 | ||
| 61 | |||
| 62 | 50 | ||
| 63 | /* mask with all color bits */ | 51 | /* mask with all color bits */ |
| 64 | #define maskcolors (bitmask(BLACKBIT) | WHITEBITS) | 52 | #define maskcolors (bitmask(BLACKBIT) | WHITEBITS) |
| @@ -105,7 +93,7 @@ | |||
| 105 | #define markobjectN(g,t) { if (t) markobject(g,t); } | 93 | #define markobjectN(g,t) { if (t) markobject(g,t); } |
| 106 | 94 | ||
| 107 | static void reallymarkobject (global_State *g, GCObject *o); | 95 | static void reallymarkobject (global_State *g, GCObject *o); |
| 108 | static lu_mem atomic (lua_State *L); | 96 | static l_mem atomic (lua_State *L); |
| 109 | static void entersweep (lua_State *L); | 97 | static void entersweep (lua_State *L); |
| 110 | 98 | ||
| 111 | 99 | ||
| @@ -259,7 +247,7 @@ GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) { | |||
| 259 | global_State *g = G(L); | 247 | global_State *g = G(L); |
| 260 | char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); | 248 | char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); |
| 261 | GCObject *o = cast(GCObject *, p + offset); | 249 | GCObject *o = cast(GCObject *, p + offset); |
| 262 | g->totalobjs++; | 250 | g->GCdebt++; |
| 263 | o->marked = luaC_white(g); | 251 | o->marked = luaC_white(g); |
| 264 | o->tt = tt; | 252 | o->tt = tt; |
| 265 | o->next = g->allgc; | 253 | o->next = g->allgc; |
| @@ -268,6 +256,9 @@ GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) { | |||
| 268 | } | 256 | } |
| 269 | 257 | ||
| 270 | 258 | ||
| 259 | /* | ||
| 260 | ** create a new collectable object with no offset. | ||
| 261 | */ | ||
| 271 | GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { | 262 | GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { |
| 272 | return luaC_newobjdt(L, tt, sz, 0); | 263 | return luaC_newobjdt(L, tt, sz, 0); |
| 273 | } | 264 | } |
| @@ -296,6 +287,7 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { | |||
| 296 | ** (only closures can), and a userdata's metatable must be a table. | 287 | ** (only closures can), and a userdata's metatable must be a table. |
| 297 | */ | 288 | */ |
| 298 | static void reallymarkobject (global_State *g, GCObject *o) { | 289 | static void reallymarkobject (global_State *g, GCObject *o) { |
| 290 | g->marked++; | ||
| 299 | switch (o->tt) { | 291 | switch (o->tt) { |
| 300 | case LUA_VSHRSTR: | 292 | case LUA_VSHRSTR: |
| 301 | case LUA_VLNGSTR: { | 293 | case LUA_VLNGSTR: { |
| @@ -343,9 +335,9 @@ static void markmt (global_State *g) { | |||
| 343 | /* | 335 | /* |
| 344 | ** mark all objects in list of being-finalized | 336 | ** mark all objects in list of being-finalized |
| 345 | */ | 337 | */ |
| 346 | static lu_mem markbeingfnz (global_State *g) { | 338 | static l_mem markbeingfnz (global_State *g) { |
| 347 | GCObject *o; | 339 | GCObject *o; |
| 348 | lu_mem count = 0; | 340 | l_mem count = 0; |
| 349 | for (o = g->tobefnz; o != NULL; o = o->next) { | 341 | for (o = g->tobefnz; o != NULL; o = o->next) { |
| 350 | count++; | 342 | count++; |
| 351 | markobject(g, o); | 343 | markobject(g, o); |
| @@ -365,12 +357,11 @@ static lu_mem markbeingfnz (global_State *g) { | |||
| 365 | ** upvalues, as they have nothing to be checked. (If the thread gets an | 357 | ** upvalues, as they have nothing to be checked. (If the thread gets an |
| 366 | ** upvalue later, it will be linked in the list again.) | 358 | ** upvalue later, it will be linked in the list again.) |
| 367 | */ | 359 | */ |
| 368 | static int remarkupvals (global_State *g) { | 360 | static l_mem remarkupvals (global_State *g) { |
| 361 | l_mem work = 0; | ||
| 369 | lua_State *thread; | 362 | lua_State *thread; |
| 370 | lua_State **p = &g->twups; | 363 | lua_State **p = &g->twups; |
| 371 | int work = 0; /* estimate of how much work was done here */ | ||
| 372 | while ((thread = *p) != NULL) { | 364 | while ((thread = *p) != NULL) { |
| 373 | work++; | ||
| 374 | if (!iswhite(thread) && thread->openupval != NULL) | 365 | if (!iswhite(thread) && thread->openupval != NULL) |
| 375 | p = &thread->twups; /* keep marked thread with upvalues in the list */ | 366 | p = &thread->twups; /* keep marked thread with upvalues in the list */ |
| 376 | else { /* thread is not marked or without upvalues */ | 367 | else { /* thread is not marked or without upvalues */ |
| @@ -380,13 +371,13 @@ static int remarkupvals (global_State *g) { | |||
| 380 | thread->twups = thread; /* mark that it is out of list */ | 371 | thread->twups = thread; /* mark that it is out of list */ |
| 381 | for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { | 372 | for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { |
| 382 | lua_assert(getage(uv) <= getage(thread)); | 373 | lua_assert(getage(uv) <= getage(thread)); |
| 383 | work++; | ||
| 384 | if (!iswhite(uv)) { /* upvalue already visited? */ | 374 | if (!iswhite(uv)) { /* upvalue already visited? */ |
| 385 | lua_assert(upisopen(uv) && isgray(uv)); | 375 | lua_assert(upisopen(uv) && isgray(uv)); |
| 386 | markvalue(g, uv->v.p); /* mark its value */ | 376 | markvalue(g, uv->v.p); /* mark its value */ |
| 387 | } | 377 | } |
| 388 | } | 378 | } |
| 389 | } | 379 | } |
| 380 | work++; | ||
| 390 | } | 381 | } |
| 391 | return work; | 382 | return work; |
| 392 | } | 383 | } |
| @@ -399,10 +390,15 @@ static void cleargraylists (global_State *g) { | |||
| 399 | 390 | ||
| 400 | 391 | ||
| 401 | /* | 392 | /* |
| 402 | ** mark root set and reset all gray lists, to start a new collection | 393 | ** mark root set and reset all gray lists, to start a new collection. |
| 394 | ** 'marked' is initialized with the number of fixed objects in the state, | ||
| 395 | ** to count the total number of live objects during a cycle. (That is | ||
| 396 | ** the metafield names, plus the reserved words, plus "_ENV" plus the | ||
| 397 | ** memory-error message.) | ||
| 403 | */ | 398 | */ |
| 404 | static void restartcollection (global_State *g) { | 399 | static void restartcollection (global_State *g) { |
| 405 | cleargraylists(g); | 400 | cleargraylists(g); |
| 401 | g->marked = TM_N + NUM_RESERVED + 2; | ||
| 406 | markobject(g, g->mainthread); | 402 | markobject(g, g->mainthread); |
| 407 | markvalue(g, &g->l_registry); | 403 | markvalue(g, &g->l_registry); |
| 408 | markmt(g); | 404 | markmt(g); |
| @@ -540,7 +536,7 @@ static void traversestrongtable (global_State *g, Table *h) { | |||
| 540 | } | 536 | } |
| 541 | 537 | ||
| 542 | 538 | ||
| 543 | static lu_mem traversetable (global_State *g, Table *h) { | 539 | static void traversetable (global_State *g, Table *h) { |
| 544 | const char *weakkey, *weakvalue; | 540 | const char *weakkey, *weakvalue; |
| 545 | const TValue *mode = gfasttm(g, h->metatable, TM_MODE); | 541 | const TValue *mode = gfasttm(g, h->metatable, TM_MODE); |
| 546 | markobjectN(g, h->metatable); | 542 | markobjectN(g, h->metatable); |
| @@ -557,17 +553,15 @@ static lu_mem traversetable (global_State *g, Table *h) { | |||
| 557 | } | 553 | } |
| 558 | else /* not weak */ | 554 | else /* not weak */ |
| 559 | traversestrongtable(g, h); | 555 | traversestrongtable(g, h); |
| 560 | return 1 + h->alimit + 2 * allocsizenode(h); | ||
| 561 | } | 556 | } |
| 562 | 557 | ||
| 563 | 558 | ||
| 564 | static int traverseudata (global_State *g, Udata *u) { | 559 | static void traverseudata (global_State *g, Udata *u) { |
| 565 | int i; | 560 | int i; |
| 566 | markobjectN(g, u->metatable); /* mark its metatable */ | 561 | markobjectN(g, u->metatable); /* mark its metatable */ |
| 567 | for (i = 0; i < u->nuvalue; i++) | 562 | for (i = 0; i < u->nuvalue; i++) |
| 568 | markvalue(g, &u->uv[i].uv); | 563 | markvalue(g, &u->uv[i].uv); |
| 569 | genlink(g, obj2gco(u)); | 564 | genlink(g, obj2gco(u)); |
| 570 | return 1 + u->nuvalue; | ||
| 571 | } | 565 | } |
| 572 | 566 | ||
| 573 | 567 | ||
| @@ -576,7 +570,7 @@ static int traverseudata (global_State *g, Udata *u) { | |||
| 576 | ** arrays can be larger than needed; the extra slots are filled with | 570 | ** arrays can be larger than needed; the extra slots are filled with |
| 577 | ** NULL, so the use of 'markobjectN') | 571 | ** NULL, so the use of 'markobjectN') |
| 578 | */ | 572 | */ |
| 579 | static int traverseproto (global_State *g, Proto *f) { | 573 | static void traverseproto (global_State *g, Proto *f) { |
| 580 | int i; | 574 | int i; |
| 581 | markobjectN(g, f->source); | 575 | markobjectN(g, f->source); |
| 582 | for (i = 0; i < f->sizek; i++) /* mark literals */ | 576 | for (i = 0; i < f->sizek; i++) /* mark literals */ |
| @@ -587,29 +581,26 @@ static int traverseproto (global_State *g, Proto *f) { | |||
| 587 | markobjectN(g, f->p[i]); | 581 | markobjectN(g, f->p[i]); |
| 588 | for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ | 582 | for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ |
| 589 | markobjectN(g, f->locvars[i].varname); | 583 | markobjectN(g, f->locvars[i].varname); |
| 590 | return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; | ||
| 591 | } | 584 | } |
| 592 | 585 | ||
| 593 | 586 | ||
| 594 | static int traverseCclosure (global_State *g, CClosure *cl) { | 587 | static void traverseCclosure (global_State *g, CClosure *cl) { |
| 595 | int i; | 588 | int i; |
| 596 | for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ | 589 | for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ |
| 597 | markvalue(g, &cl->upvalue[i]); | 590 | markvalue(g, &cl->upvalue[i]); |
| 598 | return 1 + cl->nupvalues; | ||
| 599 | } | 591 | } |
| 600 | 592 | ||
| 601 | /* | 593 | /* |
| 602 | ** Traverse a Lua closure, marking its prototype and its upvalues. | 594 | ** Traverse a Lua closure, marking its prototype and its upvalues. |
| 603 | ** (Both can be NULL while closure is being created.) | 595 | ** (Both can be NULL while closure is being created.) |
| 604 | */ | 596 | */ |
| 605 | static int traverseLclosure (global_State *g, LClosure *cl) { | 597 | static void traverseLclosure (global_State *g, LClosure *cl) { |
| 606 | int i; | 598 | int i; |
| 607 | markobjectN(g, cl->p); /* mark its prototype */ | 599 | markobjectN(g, cl->p); /* mark its prototype */ |
| 608 | for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ | 600 | for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ |
| 609 | UpVal *uv = cl->upvals[i]; | 601 | UpVal *uv = cl->upvals[i]; |
| 610 | markobjectN(g, uv); /* mark upvalue */ | 602 | markobjectN(g, uv); /* mark upvalue */ |
| 611 | } | 603 | } |
| 612 | return 1 + cl->nupvalues; | ||
| 613 | } | 604 | } |
| 614 | 605 | ||
| 615 | 606 | ||
| @@ -625,13 +616,13 @@ static int traverseLclosure (global_State *g, LClosure *cl) { | |||
| 625 | ** (which can only happen in generational mode) or if the traverse is in | 616 | ** (which can only happen in generational mode) or if the traverse is in |
| 626 | ** the propagate phase (which can only happen in incremental mode). | 617 | ** the propagate phase (which can only happen in incremental mode). |
| 627 | */ | 618 | */ |
| 628 | static int traversethread (global_State *g, lua_State *th) { | 619 | static void traversethread (global_State *g, lua_State *th) { |
| 629 | UpVal *uv; | 620 | UpVal *uv; |
| 630 | StkId o = th->stack.p; | 621 | StkId o = th->stack.p; |
| 631 | if (isold(th) || g->gcstate == GCSpropagate) | 622 | if (isold(th) || g->gcstate == GCSpropagate) |
| 632 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ | 623 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ |
| 633 | if (o == NULL) | 624 | if (o == NULL) |
| 634 | return 1; /* stack not completely built yet */ | 625 | return; /* stack not completely built yet */ |
| 635 | lua_assert(g->gcstate == GCSatomic || | 626 | lua_assert(g->gcstate == GCSatomic || |
| 636 | th->openupval == NULL || isintwups(th)); | 627 | th->openupval == NULL || isintwups(th)); |
| 637 | for (; o < th->top.p; o++) /* mark live elements in the stack */ | 628 | for (; o < th->top.p; o++) /* mark live elements in the stack */ |
| @@ -649,34 +640,35 @@ static int traversethread (global_State *g, lua_State *th) { | |||
| 649 | } | 640 | } |
| 650 | else if (!g->gcemergency) | 641 | else if (!g->gcemergency) |
| 651 | luaD_shrinkstack(th); /* do not change stack in emergency cycle */ | 642 | luaD_shrinkstack(th); /* do not change stack in emergency cycle */ |
| 652 | return 1 + stacksize(th); | ||
| 653 | } | 643 | } |
| 654 | 644 | ||
| 655 | 645 | ||
| 656 | /* | 646 | /* |
| 657 | ** traverse one gray object, turning it to black. | 647 | ** traverse one gray object, turning it to black. |
| 658 | */ | 648 | */ |
| 659 | static lu_mem propagatemark (global_State *g) { | 649 | static void propagatemark (global_State *g) { |
| 660 | GCObject *o = g->gray; | 650 | GCObject *o = g->gray; |
| 661 | nw2black(o); | 651 | nw2black(o); |
| 662 | g->gray = *getgclist(o); /* remove from 'gray' list */ | 652 | g->gray = *getgclist(o); /* remove from 'gray' list */ |
| 663 | switch (o->tt) { | 653 | switch (o->tt) { |
| 664 | case LUA_VTABLE: return traversetable(g, gco2t(o)); | 654 | case LUA_VTABLE: traversetable(g, gco2t(o)); break; |
| 665 | case LUA_VUSERDATA: return traverseudata(g, gco2u(o)); | 655 | case LUA_VUSERDATA: traverseudata(g, gco2u(o)); break; |
| 666 | case LUA_VLCL: return traverseLclosure(g, gco2lcl(o)); | 656 | case LUA_VLCL: traverseLclosure(g, gco2lcl(o)); break; |
| 667 | case LUA_VCCL: return traverseCclosure(g, gco2ccl(o)); | 657 | case LUA_VCCL: traverseCclosure(g, gco2ccl(o)); break; |
| 668 | case LUA_VPROTO: return traverseproto(g, gco2p(o)); | 658 | case LUA_VPROTO: traverseproto(g, gco2p(o)); break; |
| 669 | case LUA_VTHREAD: return traversethread(g, gco2th(o)); | 659 | case LUA_VTHREAD: traversethread(g, gco2th(o)); break; |
| 670 | default: lua_assert(0); return 0; | 660 | default: lua_assert(0); |
| 671 | } | 661 | } |
| 672 | } | 662 | } |
| 673 | 663 | ||
| 674 | 664 | ||
| 675 | static lu_mem propagateall (global_State *g) { | 665 | static l_mem propagateall (global_State *g) { |
| 676 | lu_mem tot = 0; | 666 | l_mem work = 0; |
| 677 | while (g->gray) | 667 | while (g->gray) { |
| 678 | tot += propagatemark(g); | 668 | propagatemark(g); |
| 679 | return tot; | 669 | work++; |
| 670 | } | ||
| 671 | return work; | ||
| 680 | } | 672 | } |
| 681 | 673 | ||
| 682 | 674 | ||
| @@ -685,10 +677,10 @@ static lu_mem propagateall (global_State *g) { | |||
| 685 | ** Repeat until it converges, that is, nothing new is marked. 'dir' | 677 | ** Repeat until it converges, that is, nothing new is marked. 'dir' |
| 686 | ** inverts the direction of the traversals, trying to speed up | 678 | ** inverts the direction of the traversals, trying to speed up |
| 687 | ** convergence on chains in the same table. | 679 | ** convergence on chains in the same table. |
| 688 | ** | ||
| 689 | */ | 680 | */ |
| 690 | static void convergeephemerons (global_State *g) { | 681 | static l_mem convergeephemerons (global_State *g) { |
| 691 | int changed; | 682 | int changed; |
| 683 | l_mem work = 0; | ||
| 692 | int dir = 0; | 684 | int dir = 0; |
| 693 | do { | 685 | do { |
| 694 | GCObject *w; | 686 | GCObject *w; |
| @@ -703,9 +695,11 @@ static void convergeephemerons (global_State *g) { | |||
| 703 | propagateall(g); /* propagate changes */ | 695 | propagateall(g); /* propagate changes */ |
| 704 | changed = 1; /* will have to revisit all ephemeron tables */ | 696 | changed = 1; /* will have to revisit all ephemeron tables */ |
| 705 | } | 697 | } |
| 698 | work++; | ||
| 706 | } | 699 | } |
| 707 | dir = !dir; /* invert direction next time */ | 700 | dir = !dir; /* invert direction next time */ |
| 708 | } while (changed); /* repeat until no more changes */ | 701 | } while (changed); /* repeat until no more changes */ |
| 702 | return work; | ||
| 709 | } | 703 | } |
| 710 | 704 | ||
| 711 | /* }====================================================== */ | 705 | /* }====================================================== */ |
| @@ -721,7 +715,8 @@ static void convergeephemerons (global_State *g) { | |||
| 721 | /* | 715 | /* |
| 722 | ** clear entries with unmarked keys from all weaktables in list 'l' | 716 | ** clear entries with unmarked keys from all weaktables in list 'l' |
| 723 | */ | 717 | */ |
| 724 | static void clearbykeys (global_State *g, GCObject *l) { | 718 | static l_mem clearbykeys (global_State *g, GCObject *l) { |
| 719 | l_mem work = 0; | ||
| 725 | for (; l; l = gco2t(l)->gclist) { | 720 | for (; l; l = gco2t(l)->gclist) { |
| 726 | Table *h = gco2t(l); | 721 | Table *h = gco2t(l); |
| 727 | Node *limit = gnodelast(h); | 722 | Node *limit = gnodelast(h); |
| @@ -732,7 +727,9 @@ static void clearbykeys (global_State *g, GCObject *l) { | |||
| 732 | if (isempty(gval(n))) /* is entry empty? */ | 727 | if (isempty(gval(n))) /* is entry empty? */ |
| 733 | clearkey(n); /* clear its key */ | 728 | clearkey(n); /* clear its key */ |
| 734 | } | 729 | } |
| 730 | work++; | ||
| 735 | } | 731 | } |
| 732 | return work; | ||
| 736 | } | 733 | } |
| 737 | 734 | ||
| 738 | 735 | ||
| @@ -740,7 +737,8 @@ static void clearbykeys (global_State *g, GCObject *l) { | |||
| 740 | ** clear entries with unmarked values from all weaktables in list 'l' up | 737 | ** clear entries with unmarked values from all weaktables in list 'l' up |
| 741 | ** to element 'f' | 738 | ** to element 'f' |
| 742 | */ | 739 | */ |
| 743 | static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { | 740 | static l_mem clearbyvalues (global_State *g, GCObject *l, GCObject *f) { |
| 741 | l_mem work = 0; | ||
| 744 | for (; l != f; l = gco2t(l)->gclist) { | 742 | for (; l != f; l = gco2t(l)->gclist) { |
| 745 | Table *h = gco2t(l); | 743 | Table *h = gco2t(l); |
| 746 | Node *n, *limit = gnodelast(h); | 744 | Node *n, *limit = gnodelast(h); |
| @@ -757,7 +755,9 @@ static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { | |||
| 757 | if (isempty(gval(n))) /* is entry empty? */ | 755 | if (isempty(gval(n))) /* is entry empty? */ |
| 758 | clearkey(n); /* clear its key */ | 756 | clearkey(n); /* clear its key */ |
| 759 | } | 757 | } |
| 758 | work++; | ||
| 760 | } | 759 | } |
| 760 | return work; | ||
| 761 | } | 761 | } |
| 762 | 762 | ||
| 763 | 763 | ||
| @@ -819,10 +819,9 @@ static void freeobj (lua_State *L, GCObject *o) { | |||
| 819 | ** objects, where a dead object is one marked with the old (non current) | 819 | ** objects, where a dead object is one marked with the old (non current) |
| 820 | ** white; change all non-dead objects back to white, preparing for next | 820 | ** white; change all non-dead objects back to white, preparing for next |
| 821 | ** collection cycle. Return where to continue the traversal or NULL if | 821 | ** collection cycle. Return where to continue the traversal or NULL if |
| 822 | ** list is finished. ('*countout' gets the number of elements traversed.) | 822 | ** list is finished. |
| 823 | */ | 823 | */ |
| 824 | static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, | 824 | static GCObject **sweeplist (lua_State *L, GCObject **p, int countin) { |
| 825 | int *countout) { | ||
| 826 | global_State *g = G(L); | 825 | global_State *g = G(L); |
| 827 | int ow = otherwhite(g); | 826 | int ow = otherwhite(g); |
| 828 | int i; | 827 | int i; |
| @@ -839,8 +838,6 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, | |||
| 839 | p = &curr->next; /* go to next element */ | 838 | p = &curr->next; /* go to next element */ |
| 840 | } | 839 | } |
| 841 | } | 840 | } |
| 842 | if (countout) | ||
| 843 | *countout = i; /* number of elements traversed */ | ||
| 844 | return (*p == NULL) ? NULL : p; | 841 | return (*p == NULL) ? NULL : p; |
| 845 | } | 842 | } |
| 846 | 843 | ||
| @@ -851,7 +848,7 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, | |||
| 851 | static GCObject **sweeptolive (lua_State *L, GCObject **p) { | 848 | static GCObject **sweeptolive (lua_State *L, GCObject **p) { |
| 852 | GCObject **old = p; | 849 | GCObject **old = p; |
| 853 | do { | 850 | do { |
| 854 | p = sweeplist(L, p, 1, NULL); | 851 | p = sweeplist(L, p, 1); |
| 855 | } while (p == old); | 852 | } while (p == old); |
| 856 | return p; | 853 | return p; |
| 857 | } | 854 | } |
| @@ -870,11 +867,8 @@ static GCObject **sweeptolive (lua_State *L, GCObject **p) { | |||
| 870 | */ | 867 | */ |
| 871 | static void checkSizes (lua_State *L, global_State *g) { | 868 | static void checkSizes (lua_State *L, global_State *g) { |
| 872 | if (!g->gcemergency) { | 869 | if (!g->gcemergency) { |
| 873 | if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */ | 870 | if (g->strt.nuse < g->strt.size / 4) /* string table too big? */ |
| 874 | l_mem olddebt = g->GCdebt; | ||
| 875 | luaS_resize(L, g->strt.size / 2); | 871 | luaS_resize(L, g->strt.size / 2); |
| 876 | g->GCestimate += g->GCdebt - olddebt; /* correct estimate */ | ||
| 877 | } | ||
| 878 | } | 872 | } |
| 879 | } | 873 | } |
| 880 | 874 | ||
| @@ -935,12 +929,11 @@ static void GCTM (lua_State *L) { | |||
| 935 | /* | 929 | /* |
| 936 | ** Call a few finalizers | 930 | ** Call a few finalizers |
| 937 | */ | 931 | */ |
| 938 | static int runafewfinalizers (lua_State *L, int n) { | 932 | static void runafewfinalizers (lua_State *L, int n) { |
| 939 | global_State *g = G(L); | 933 | global_State *g = G(L); |
| 940 | int i; | 934 | int i; |
| 941 | for (i = 0; i < n && g->tobefnz; i++) | 935 | for (i = 0; i < n && g->tobefnz; i++) |
| 942 | GCTM(L); /* call one finalizer */ | 936 | GCTM(L); /* call one finalizer */ |
| 943 | return i; | ||
| 944 | } | 937 | } |
| 945 | 938 | ||
| 946 | 939 | ||
| @@ -1052,19 +1045,16 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { | |||
| 1052 | 1045 | ||
| 1053 | /* | 1046 | /* |
| 1054 | ** Set the "time" to wait before starting a new GC cycle; cycle will | 1047 | ** Set the "time" to wait before starting a new GC cycle; cycle will |
| 1055 | ** start when memory use hits the threshold of ('estimate' * pause / | 1048 | ** start when number of objects in use hits the threshold of |
| 1056 | ** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, | 1049 | ** approximately ('marked' * pause / 100). (A direct multiplication |
| 1057 | ** because Lua cannot even start with less than PAUSEADJ bytes). | 1050 | ** by 'pause' may overflow, and a direct division by 100 may undeflow |
| 1051 | ** to zero. So, the division is done in two steps. 8 * 12 is near 100 | ||
| 1052 | ** and the division by 8 is cheap.) | ||
| 1058 | */ | 1053 | */ |
| 1059 | static void setpause (global_State *g) { | 1054 | static void setpause (global_State *g) { |
| 1060 | l_mem threshold, debt; | 1055 | unsigned int pause = getgcparam(g->gcpause); |
| 1061 | int pause = getgcparam(g->gcpause); | 1056 | lu_mem threshold = g->marked / 8 * pause / 12; |
| 1062 | l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ | 1057 | l_mem debt = gettotalobjs(g) - threshold; |
| 1063 | lua_assert(estimate > 0); | ||
| 1064 | threshold = (pause < MAX_LMEM / estimate) /* overflow? */ | ||
| 1065 | ? estimate * pause /* no overflow */ | ||
| 1066 | : MAX_LMEM; /* overflow; truncate to maximum */ | ||
| 1067 | debt = gettotalbytes(g) - threshold; | ||
| 1068 | if (debt > 0) debt = 0; | 1058 | if (debt > 0) debt = 0; |
| 1069 | luaE_setdebt(g, debt); | 1059 | luaE_setdebt(g, debt); |
| 1070 | } | 1060 | } |
| @@ -1306,17 +1296,17 @@ static void atomic2gen (lua_State *L, global_State *g) { | |||
| 1306 | 1296 | ||
| 1307 | g->gckind = KGC_GEN; | 1297 | g->gckind = KGC_GEN; |
| 1308 | g->lastatomic = 0; | 1298 | g->lastatomic = 0; |
| 1309 | g->GCestimate = gettotalbytes(g); /* base for memory control */ | 1299 | g->GCestimate = gettotalobjs(g); /* base for memory control */ |
| 1310 | finishgencycle(L, g); | 1300 | finishgencycle(L, g); |
| 1311 | } | 1301 | } |
| 1312 | 1302 | ||
| 1313 | 1303 | ||
| 1314 | /* | 1304 | /* |
| 1315 | ** Set debt for the next minor collection, which will happen when | 1305 | ** Set debt for the next minor collection, which will happen when |
| 1316 | ** memory grows 'genminormul'%. | 1306 | ** total number of objects grows 'genminormul'%. |
| 1317 | */ | 1307 | */ |
| 1318 | static void setminordebt (global_State *g) { | 1308 | static void setminordebt (global_State *g) { |
| 1319 | luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); | 1309 | luaE_setdebt(g, -(cast(l_mem, (gettotalobjs(g) / 100)) * g->genminormul)); |
| 1320 | } | 1310 | } |
| 1321 | 1311 | ||
| 1322 | 1312 | ||
| @@ -1326,14 +1316,12 @@ static void setminordebt (global_State *g) { | |||
| 1326 | ** are cleared. Then, turn all objects into old and finishes the | 1316 | ** are cleared. Then, turn all objects into old and finishes the |
| 1327 | ** collection. | 1317 | ** collection. |
| 1328 | */ | 1318 | */ |
| 1329 | static lu_mem entergen (lua_State *L, global_State *g) { | 1319 | static void entergen (lua_State *L, global_State *g) { |
| 1330 | lu_mem numobjs; | ||
| 1331 | luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ | 1320 | luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ |
| 1332 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | 1321 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ |
| 1333 | numobjs = atomic(L); /* propagates all and then do the atomic stuff */ | 1322 | atomic(L); /* propagates all and then do the atomic stuff */ |
| 1334 | atomic2gen(L, g); | 1323 | atomic2gen(L, g); |
| 1335 | setminordebt(g); /* set debt assuming next cycle will be minor */ | 1324 | setminordebt(g); /* set debt assuming next cycle will be minor */ |
| 1336 | return numobjs; | ||
| 1337 | } | 1325 | } |
| 1338 | 1326 | ||
| 1339 | 1327 | ||
| @@ -1372,9 +1360,9 @@ void luaC_changemode (lua_State *L, int newmode) { | |||
| 1372 | /* | 1360 | /* |
| 1373 | ** Does a full collection in generational mode. | 1361 | ** Does a full collection in generational mode. |
| 1374 | */ | 1362 | */ |
| 1375 | static lu_mem fullgen (lua_State *L, global_State *g) { | 1363 | static void fullgen (lua_State *L, global_State *g) { |
| 1376 | enterinc(g); | 1364 | enterinc(g); |
| 1377 | return entergen(L, g); | 1365 | entergen(L, g); |
| 1378 | } | 1366 | } |
| 1379 | 1367 | ||
| 1380 | 1368 | ||
| @@ -1400,22 +1388,22 @@ static lu_mem fullgen (lua_State *L, global_State *g) { | |||
| 1400 | ** ('g->lastatomic != 0' also means that the last collection was bad.) | 1388 | ** ('g->lastatomic != 0' also means that the last collection was bad.) |
| 1401 | */ | 1389 | */ |
| 1402 | static void stepgenfull (lua_State *L, global_State *g) { | 1390 | static void stepgenfull (lua_State *L, global_State *g) { |
| 1403 | lu_mem newatomic; /* count of traversed objects */ | ||
| 1404 | lu_mem lastatomic = g->lastatomic; /* count from last collection */ | 1391 | lu_mem lastatomic = g->lastatomic; /* count from last collection */ |
| 1405 | if (g->gckind == KGC_GEN) /* still in generational mode? */ | 1392 | if (g->gckind == KGC_GEN) /* still in generational mode? */ |
| 1406 | enterinc(g); /* enter incremental mode */ | 1393 | enterinc(g); /* enter incremental mode */ |
| 1407 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ | 1394 | luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ |
| 1408 | newatomic = atomic(L); /* mark everybody */ | 1395 | g->marked = 0; |
| 1409 | if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */ | 1396 | atomic(L); /* mark everybody */ |
| 1397 | if (g->marked < lastatomic + (lastatomic >> 3)) { /* good collection? */ | ||
| 1410 | atomic2gen(L, g); /* return to generational mode */ | 1398 | atomic2gen(L, g); /* return to generational mode */ |
| 1411 | setminordebt(g); | 1399 | setminordebt(g); |
| 1412 | } | 1400 | } |
| 1413 | else { /* another bad collection; stay in incremental mode */ | 1401 | else { /* another bad collection; stay in incremental mode */ |
| 1414 | g->GCestimate = gettotalbytes(g); /* first estimate */; | 1402 | g->GCestimate = gettotalobjs(g); /* first estimate */; |
| 1403 | g->lastatomic = g->marked; | ||
| 1415 | entersweep(L); | 1404 | entersweep(L); |
| 1416 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ | 1405 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ |
| 1417 | setpause(g); | 1406 | setpause(g); |
| 1418 | g->lastatomic = newatomic; | ||
| 1419 | } | 1407 | } |
| 1420 | } | 1408 | } |
| 1421 | 1409 | ||
| @@ -1443,21 +1431,23 @@ static void genstep (lua_State *L, global_State *g) { | |||
| 1443 | if (g->lastatomic != 0) /* last collection was a bad one? */ | 1431 | if (g->lastatomic != 0) /* last collection was a bad one? */ |
| 1444 | stepgenfull(L, g); /* do a full step */ | 1432 | stepgenfull(L, g); /* do a full step */ |
| 1445 | else { | 1433 | else { |
| 1446 | lu_mem majorbase = g->GCestimate; /* memory after last major collection */ | 1434 | l_mem majorbase = g->GCestimate; /* objects after last major collection */ |
| 1447 | lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); | 1435 | l_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); |
| 1448 | if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) { | 1436 | if (g->GCdebt > 0 && gettotalobjs(g) > majorbase + majorinc) { |
| 1449 | lu_mem numobjs = fullgen(L, g); /* do a major collection */ | 1437 | g->marked = 0; |
| 1450 | if (gettotalbytes(g) < majorbase + (majorinc / 2)) { | 1438 | fullgen(L, g); /* do a major collection */ |
| 1451 | /* collected at least half of memory growth since last major | 1439 | if (gettotalobjs(g) < majorbase + (majorinc / 2)) { |
| 1440 | /* collected at least half of object growth since last major | ||
| 1452 | collection; keep doing minor collections. */ | 1441 | collection; keep doing minor collections. */ |
| 1453 | lua_assert(g->lastatomic == 0); | 1442 | lua_assert(g->lastatomic == 0); |
| 1454 | } | 1443 | } |
| 1455 | else { /* bad collection */ | 1444 | else { /* bad collection */ |
| 1456 | g->lastatomic = numobjs; /* signal that last collection was bad */ | 1445 | g->lastatomic = g->marked; /* signal that last collection was bad */ |
| 1457 | setpause(g); /* do a long wait for next (major) collection */ | 1446 | setpause(g); /* do a long wait for next (major) collection */ |
| 1458 | } | 1447 | } |
| 1459 | } | 1448 | } |
| 1460 | else { /* regular case; do a minor collection */ | 1449 | else { /* regular case; do a minor collection */ |
| 1450 | g->marked = 0; | ||
| 1461 | youngcollection(L, g); | 1451 | youngcollection(L, g); |
| 1462 | setminordebt(g); | 1452 | setminordebt(g); |
| 1463 | g->GCestimate = majorbase; /* preserve base value */ | 1453 | g->GCestimate = majorbase; /* preserve base value */ |
| @@ -1522,9 +1512,9 @@ void luaC_freeallobjects (lua_State *L) { | |||
| 1522 | } | 1512 | } |
| 1523 | 1513 | ||
| 1524 | 1514 | ||
| 1525 | static lu_mem atomic (lua_State *L) { | 1515 | static l_mem atomic (lua_State *L) { |
| 1516 | l_mem work = 0; | ||
| 1526 | global_State *g = G(L); | 1517 | global_State *g = G(L); |
| 1527 | lu_mem work = 0; | ||
| 1528 | GCObject *origweak, *origall; | 1518 | GCObject *origweak, *origall; |
| 1529 | GCObject *grayagain = g->grayagain; /* save original list */ | 1519 | GCObject *grayagain = g->grayagain; /* save original list */ |
| 1530 | g->grayagain = NULL; | 1520 | g->grayagain = NULL; |
| @@ -1541,50 +1531,47 @@ static lu_mem atomic (lua_State *L) { | |||
| 1541 | work += propagateall(g); /* propagate changes */ | 1531 | work += propagateall(g); /* propagate changes */ |
| 1542 | g->gray = grayagain; | 1532 | g->gray = grayagain; |
| 1543 | work += propagateall(g); /* traverse 'grayagain' list */ | 1533 | work += propagateall(g); /* traverse 'grayagain' list */ |
| 1544 | convergeephemerons(g); | 1534 | work += convergeephemerons(g); |
| 1545 | /* at this point, all strongly accessible objects are marked. */ | 1535 | /* at this point, all strongly accessible objects are marked. */ |
| 1546 | /* Clear values from weak tables, before checking finalizers */ | 1536 | /* Clear values from weak tables, before checking finalizers */ |
| 1547 | clearbyvalues(g, g->weak, NULL); | 1537 | work += clearbyvalues(g, g->weak, NULL); |
| 1548 | clearbyvalues(g, g->allweak, NULL); | 1538 | work += clearbyvalues(g, g->allweak, NULL); |
| 1549 | origweak = g->weak; origall = g->allweak; | 1539 | origweak = g->weak; origall = g->allweak; |
| 1550 | separatetobefnz(g, 0); /* separate objects to be finalized */ | 1540 | separatetobefnz(g, 0); /* separate objects to be finalized */ |
| 1551 | work += markbeingfnz(g); /* mark objects that will be finalized */ | 1541 | work += markbeingfnz(g); /* mark objects that will be finalized */ |
| 1552 | work += propagateall(g); /* remark, to propagate 'resurrection' */ | 1542 | work += propagateall(g); /* remark, to propagate 'resurrection' */ |
| 1553 | convergeephemerons(g); | 1543 | work += convergeephemerons(g); |
| 1554 | /* at this point, all resurrected objects are marked. */ | 1544 | /* at this point, all resurrected objects are marked. */ |
| 1555 | /* remove dead objects from weak tables */ | 1545 | /* remove dead objects from weak tables */ |
| 1556 | clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */ | 1546 | work += clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron */ |
| 1557 | clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */ | 1547 | work += clearbykeys(g, g->allweak); /* clear keys from all 'allweak' */ |
| 1558 | /* clear values from resurrected weak tables */ | 1548 | /* clear values from resurrected weak tables */ |
| 1559 | clearbyvalues(g, g->weak, origweak); | 1549 | work += clearbyvalues(g, g->weak, origweak); |
| 1560 | clearbyvalues(g, g->allweak, origall); | 1550 | work += clearbyvalues(g, g->allweak, origall); |
| 1561 | luaS_clearcache(g); | 1551 | luaS_clearcache(g); |
| 1562 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ | 1552 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ |
| 1563 | lua_assert(g->gray == NULL); | 1553 | lua_assert(g->gray == NULL); |
| 1564 | return work; /* estimate of slots marked by 'atomic' */ | 1554 | return work; |
| 1565 | } | 1555 | } |
| 1566 | 1556 | ||
| 1567 | 1557 | ||
| 1568 | static int sweepstep (lua_State *L, global_State *g, | 1558 | static void sweepstep (lua_State *L, global_State *g, |
| 1569 | int nextstate, GCObject **nextlist) { | 1559 | int nextstate, GCObject **nextlist) { |
| 1570 | if (g->sweepgc) { | 1560 | if (g->sweepgc) { |
| 1571 | l_mem olddebt = g->GCdebt; | 1561 | l_mem olddebt = g->GCdebt; |
| 1572 | int count; | 1562 | g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); |
| 1573 | g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count); | ||
| 1574 | g->GCestimate += g->GCdebt - olddebt; /* update estimate */ | 1563 | g->GCestimate += g->GCdebt - olddebt; /* update estimate */ |
| 1575 | return count; | ||
| 1576 | } | 1564 | } |
| 1577 | else { /* enter next state */ | 1565 | else { /* enter next state */ |
| 1578 | g->gcstate = nextstate; | 1566 | g->gcstate = nextstate; |
| 1579 | g->sweepgc = nextlist; | 1567 | g->sweepgc = nextlist; |
| 1580 | return 0; /* no work done */ | ||
| 1581 | } | 1568 | } |
| 1582 | } | 1569 | } |
| 1583 | 1570 | ||
| 1584 | 1571 | ||
| 1585 | static lu_mem singlestep (lua_State *L) { | 1572 | static l_mem singlestep (lua_State *L) { |
| 1586 | global_State *g = G(L); | 1573 | global_State *g = G(L); |
| 1587 | lu_mem work; | 1574 | l_mem work; |
| 1588 | lua_assert(!g->gcstopem); /* collector is not reentrant */ | 1575 | lua_assert(!g->gcstopem); /* collector is not reentrant */ |
| 1589 | g->gcstopem = 1; /* no emergency collections while collecting */ | 1576 | g->gcstopem = 1; /* no emergency collections while collecting */ |
| 1590 | switch (g->gcstate) { | 1577 | switch (g->gcstate) { |
| @@ -1599,26 +1586,30 @@ static lu_mem singlestep (lua_State *L) { | |||
| 1599 | g->gcstate = GCSenteratomic; /* finish propagate phase */ | 1586 | g->gcstate = GCSenteratomic; /* finish propagate phase */ |
| 1600 | work = 0; | 1587 | work = 0; |
| 1601 | } | 1588 | } |
| 1602 | else | 1589 | else { |
| 1603 | work = propagatemark(g); /* traverse one gray object */ | 1590 | propagatemark(g); /* traverse one gray object */ |
| 1591 | work = 1; | ||
| 1592 | } | ||
| 1604 | break; | 1593 | break; |
| 1605 | } | 1594 | } |
| 1606 | case GCSenteratomic: { | 1595 | case GCSenteratomic: { |
| 1607 | work = atomic(L); /* work is what was traversed by 'atomic' */ | 1596 | work = atomic(L); |
| 1608 | entersweep(L); | 1597 | entersweep(L); |
| 1609 | g->GCestimate = gettotalbytes(g); /* first estimate */; | ||
| 1610 | break; | 1598 | break; |
| 1611 | } | 1599 | } |
| 1612 | case GCSswpallgc: { /* sweep "regular" objects */ | 1600 | case GCSswpallgc: { /* sweep "regular" objects */ |
| 1613 | work = sweepstep(L, g, GCSswpfinobj, &g->finobj); | 1601 | sweepstep(L, g, GCSswpfinobj, &g->finobj); |
| 1602 | work = GCSWEEPMAX; | ||
| 1614 | break; | 1603 | break; |
| 1615 | } | 1604 | } |
| 1616 | case GCSswpfinobj: { /* sweep objects with finalizers */ | 1605 | case GCSswpfinobj: { /* sweep objects with finalizers */ |
| 1617 | work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz); | 1606 | sweepstep(L, g, GCSswptobefnz, &g->tobefnz); |
| 1607 | work = GCSWEEPMAX; | ||
| 1618 | break; | 1608 | break; |
| 1619 | } | 1609 | } |
| 1620 | case GCSswptobefnz: { /* sweep objects to be finalized */ | 1610 | case GCSswptobefnz: { /* sweep objects to be finalized */ |
| 1621 | work = sweepstep(L, g, GCSswpend, NULL); | 1611 | sweepstep(L, g, GCSswpend, NULL); |
| 1612 | work = GCSWEEPMAX; | ||
| 1622 | break; | 1613 | break; |
| 1623 | } | 1614 | } |
| 1624 | case GCSswpend: { /* finish sweeps */ | 1615 | case GCSswpend: { /* finish sweeps */ |
| @@ -1630,7 +1621,8 @@ static lu_mem singlestep (lua_State *L) { | |||
| 1630 | case GCScallfin: { /* call remaining finalizers */ | 1621 | case GCScallfin: { /* call remaining finalizers */ |
| 1631 | if (g->tobefnz && !g->gcemergency) { | 1622 | if (g->tobefnz && !g->gcemergency) { |
| 1632 | g->gcstopem = 0; /* ok collections during finalizers */ | 1623 | g->gcstopem = 0; /* ok collections during finalizers */ |
| 1633 | work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST; | 1624 | runafewfinalizers(L, GCFINMAX); |
| 1625 | work = GCFINMAX * GCFINALIZECOST; | ||
| 1634 | } | 1626 | } |
| 1635 | else { /* emergency mode or no more finalizers */ | 1627 | else { /* emergency mode or no more finalizers */ |
| 1636 | g->gcstate = GCSpause; /* finish collection */ | 1628 | g->gcstate = GCSpause; /* finish collection */ |
| @@ -1666,18 +1658,16 @@ void luaC_runtilstate (lua_State *L, int statesmask) { | |||
| 1666 | */ | 1658 | */ |
| 1667 | static void incstep (lua_State *L, global_State *g) { | 1659 | static void incstep (lua_State *L, global_State *g) { |
| 1668 | int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */ | 1660 | int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */ |
| 1669 | l_mem debt = (g->GCdebt / WORK2MEM) * stepmul; | 1661 | l_mem debt = (g->GCdebt / 100) * stepmul; |
| 1670 | l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem)) | 1662 | l_mem stepsize = cast(l_mem, 1) << g->gcstepsize; |
| 1671 | ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul | ||
| 1672 | : MAX_LMEM; /* overflow; keep maximum value */ | ||
| 1673 | do { /* repeat until pause or enough "credit" (negative debt) */ | 1663 | do { /* repeat until pause or enough "credit" (negative debt) */ |
| 1674 | lu_mem work = singlestep(L); /* perform one single step */ | 1664 | l_mem work = singlestep(L); /* perform one single step */ |
| 1675 | debt -= work; | 1665 | debt -= work; |
| 1676 | } while (debt > -stepsize && g->gcstate != GCSpause); | 1666 | } while (debt > -stepsize && g->gcstate != GCSpause); |
| 1677 | if (g->gcstate == GCSpause) | 1667 | if (g->gcstate == GCSpause) |
| 1678 | setpause(g); /* pause until next cycle */ | 1668 | setpause(g); /* pause until next cycle */ |
| 1679 | else { | 1669 | else { |
| 1680 | debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */ | 1670 | debt = (debt / stepmul) * 100; /* apply step multiplier */ |
| 1681 | luaE_setdebt(g, debt); | 1671 | luaE_setdebt(g, debt); |
| 1682 | } | 1672 | } |
| 1683 | } | 1673 | } |
| @@ -1710,9 +1700,8 @@ static void fullinc (lua_State *L, global_State *g) { | |||
| 1710 | /* finish any pending sweep phase to start a new cycle */ | 1700 | /* finish any pending sweep phase to start a new cycle */ |
| 1711 | luaC_runtilstate(L, bitmask(GCSpause)); | 1701 | luaC_runtilstate(L, bitmask(GCSpause)); |
| 1712 | luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ | 1702 | luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ |
| 1713 | /* estimate must be correct after a full GC cycle */ | ||
| 1714 | lua_assert(g->GCestimate == gettotalbytes(g)); | ||
| 1715 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ | 1703 | luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ |
| 1704 | /* estimate must be correct after a full GC cycle */ | ||
| 1716 | setpause(g); | 1705 | setpause(g); |
| 1717 | } | 1706 | } |
| 1718 | 1707 | ||
| @@ -135,10 +135,10 @@ | |||
| 135 | #define getgcparam(p) ((p) * 4) | 135 | #define getgcparam(p) ((p) * 4) |
| 136 | #define setgcparam(p,v) ((p) = (v) / 4) | 136 | #define setgcparam(p,v) ((p) = (v) / 4) |
| 137 | 137 | ||
| 138 | #define LUAI_GCMUL 100 | 138 | #define LUAI_GCMUL 300 |
| 139 | 139 | ||
| 140 | /* how much to allocate before next GC step (log2) */ | 140 | /* how many objects to allocate before next GC step (log2) */ |
| 141 | #define LUAI_GCSTEPSIZE 13 /* 8 KB */ | 141 | #define LUAI_GCSTEPSIZE 8 /* 256 objects */ |
| 142 | 142 | ||
| 143 | 143 | ||
| 144 | /* | 144 | /* |
| @@ -57,7 +57,7 @@ typedef signed char ls_byte; | |||
| 57 | ** floor of the log2 of the maximum signed value for integral type 't'. | 57 | ** floor of the log2 of the maximum signed value for integral type 't'. |
| 58 | ** (That is, maximum 'n' such that '2^n' fits in the given signed type.) | 58 | ** (That is, maximum 'n' such that '2^n' fits in the given signed type.) |
| 59 | */ | 59 | */ |
| 60 | #define log2maxs(t) (sizeof(t) * 8 - 2) | 60 | #define log2maxs(t) cast_int(sizeof(t) * 8 - 2) |
| 61 | 61 | ||
| 62 | 62 | ||
| 63 | /* | 63 | /* |
| @@ -133,7 +133,7 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) { | |||
| 133 | global_State *g = G(L); | 133 | global_State *g = G(L); |
| 134 | lua_assert((osize == 0) == (block == NULL)); | 134 | lua_assert((osize == 0) == (block == NULL)); |
| 135 | (*g->frealloc)(g->ud, block, osize, 0); | 135 | (*g->frealloc)(g->ud, block, osize, 0); |
| 136 | g->GCdebt -= osize; | 136 | g->totalbytes -= osize; |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | 139 | ||
| @@ -167,10 +167,10 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { | |||
| 167 | if (l_unlikely(newblock == NULL && nsize > 0)) { | 167 | if (l_unlikely(newblock == NULL && nsize > 0)) { |
| 168 | newblock = tryagain(L, block, osize, nsize); | 168 | newblock = tryagain(L, block, osize, nsize); |
| 169 | if (newblock == NULL) /* still no memory? */ | 169 | if (newblock == NULL) /* still no memory? */ |
| 170 | return NULL; /* do not update 'GCdebt' */ | 170 | return NULL; /* do not update 'totalbytes' */ |
| 171 | } | 171 | } |
| 172 | lua_assert((nsize == 0) == (newblock == NULL)); | 172 | lua_assert((nsize == 0) == (newblock == NULL)); |
| 173 | g->GCdebt = (g->GCdebt + nsize) - osize; | 173 | g->totalbytes += nsize - osize; |
| 174 | return newblock; | 174 | return newblock; |
| 175 | } | 175 | } |
| 176 | 176 | ||
| @@ -195,7 +195,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) { | |||
| 195 | if (newblock == NULL) | 195 | if (newblock == NULL) |
| 196 | luaM_error(L); | 196 | luaM_error(L); |
| 197 | } | 197 | } |
| 198 | g->GCdebt += size; | 198 | g->totalbytes += size; |
| 199 | return newblock; | 199 | return newblock; |
| 200 | } | 200 | } |
| 201 | } | 201 | } |
| @@ -83,15 +83,15 @@ static unsigned int luai_makeseed (lua_State *L) { | |||
| 83 | 83 | ||
| 84 | 84 | ||
| 85 | /* | 85 | /* |
| 86 | ** set GCdebt to a new value keeping the value (totalbytes + GCdebt) | 86 | ** set GCdebt to a new value keeping the value (totalobjs + GCdebt) |
| 87 | ** invariant (and avoiding underflows in 'totalbytes') | 87 | ** invariant (and avoiding underflows in 'totalobjs') |
| 88 | */ | 88 | */ |
| 89 | void luaE_setdebt (global_State *g, l_mem debt) { | 89 | void luaE_setdebt (global_State *g, l_mem debt) { |
| 90 | l_mem tb = gettotalbytes(g); | 90 | l_mem tb = gettotalobjs(g); |
| 91 | lua_assert(tb > 0); | 91 | lua_assert(tb > 0); |
| 92 | if (debt < tb - MAX_LMEM) | 92 | if (debt < tb - MAX_LMEM) |
| 93 | debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */ | 93 | debt = tb - MAX_LMEM; /* will make 'totalobjs == MAX_LMEM' */ |
| 94 | g->totalbytes = tb - debt; | 94 | g->totalobjs = tb - debt; |
| 95 | g->GCdebt = debt; | 95 | g->GCdebt = debt; |
| 96 | } | 96 | } |
| 97 | 97 | ||
| @@ -278,8 +278,8 @@ static void close_state (lua_State *L) { | |||
| 278 | } | 278 | } |
| 279 | luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); | 279 | luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); |
| 280 | freestack(L); | 280 | freestack(L); |
| 281 | lua_assert(gettotalbytes(g) == sizeof(LG)); | 281 | lua_assert(g->totalbytes == sizeof(LG)); |
| 282 | lua_assert(g->totalobjs == 1); | 282 | lua_assert(gettotalobjs(g) == 1); |
| 283 | (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ | 283 | (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ |
| 284 | } | 284 | } |
| 285 | 285 | ||
| @@ -389,6 +389,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { | |||
| 389 | g->twups = NULL; | 389 | g->twups = NULL; |
| 390 | g->totalbytes = sizeof(LG); | 390 | g->totalbytes = sizeof(LG); |
| 391 | g->totalobjs = 1; | 391 | g->totalobjs = 1; |
| 392 | g->marked = 0; | ||
| 392 | g->GCdebt = 0; | 393 | g->GCdebt = 0; |
| 393 | g->lastatomic = 0; | 394 | g->lastatomic = 0; |
| 394 | setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ | 395 | setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ |
| @@ -249,9 +249,10 @@ typedef struct CallInfo { | |||
| 249 | typedef struct global_State { | 249 | typedef struct global_State { |
| 250 | lua_Alloc frealloc; /* function to reallocate memory */ | 250 | lua_Alloc frealloc; /* function to reallocate memory */ |
| 251 | void *ud; /* auxiliary data to 'frealloc' */ | 251 | void *ud; /* auxiliary data to 'frealloc' */ |
| 252 | l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ | 252 | l_mem totalbytes; /* number of bytes currently allocated */ |
| 253 | l_mem totalobjs; /* total number of objects allocated */ | 253 | l_mem totalobjs; /* total number of objects allocated - GCdebt */ |
| 254 | l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ | 254 | l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ |
| 255 | lu_mem marked; /* number of objects marked in a GC cycle */ | ||
| 255 | lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ | 256 | lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ |
| 256 | lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */ | 257 | lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */ |
| 257 | stringtable strt; /* hash table for strings */ | 258 | stringtable strt; /* hash table for strings */ |
| @@ -386,8 +387,9 @@ union GCUnion { | |||
| 386 | #define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) | 387 | #define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) |
| 387 | 388 | ||
| 388 | 389 | ||
| 389 | /* actual number of total bytes allocated */ | 390 | /* actual number of total objects allocated */ |
| 390 | #define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt) | 391 | #define gettotalobjs(g) ((g)->totalobjs + (g)->GCdebt) |
| 392 | |||
| 391 | 393 | ||
| 392 | LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); | 394 | LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); |
| 393 | LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); | 395 | LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); |
| @@ -1027,6 +1027,16 @@ static int table_query (lua_State *L) { | |||
| 1027 | } | 1027 | } |
| 1028 | 1028 | ||
| 1029 | 1029 | ||
| 1030 | static int query_inc (lua_State *L) { | ||
| 1031 | global_State *g = G(L); | ||
| 1032 | lua_pushinteger(L, gettotalobjs(g)); | ||
| 1033 | lua_pushinteger(L, g->GCdebt); | ||
| 1034 | lua_pushinteger(L, getgcparam(g->gcpause)); | ||
| 1035 | lua_pushinteger(L, getgcparam(g->gcstepmul)); | ||
| 1036 | lua_pushinteger(L, cast(l_mem, 1) << g->gcstepsize); | ||
| 1037 | return 5; | ||
| 1038 | } | ||
| 1039 | |||
| 1030 | static int string_query (lua_State *L) { | 1040 | static int string_query (lua_State *L) { |
| 1031 | stringtable *tb = &G(L)->strt; | 1041 | stringtable *tb = &G(L)->strt; |
| 1032 | int s = cast_int(luaL_optinteger(L, 1, 0)) - 1; | 1042 | int s = cast_int(luaL_optinteger(L, 1, 0)) - 1; |
| @@ -1933,6 +1943,7 @@ static const struct luaL_Reg tests_funcs[] = { | |||
| 1933 | {"pushuserdata", pushuserdata}, | 1943 | {"pushuserdata", pushuserdata}, |
| 1934 | {"querystr", string_query}, | 1944 | {"querystr", string_query}, |
| 1935 | {"querytab", table_query}, | 1945 | {"querytab", table_query}, |
| 1946 | {"queryinc", query_inc}, | ||
| 1936 | {"ref", tref}, | 1947 | {"ref", tref}, |
| 1937 | {"resume", coresume}, | 1948 | {"resume", coresume}, |
| 1938 | {"s2d", s2d}, | 1949 | {"s2d", s2d}, |
| @@ -633,7 +633,8 @@ static int pmain (lua_State *L) { | |||
| 633 | } | 633 | } |
| 634 | luaL_openlibs(L); /* open standard libraries */ | 634 | luaL_openlibs(L); /* open standard libraries */ |
| 635 | createargtable(L, argv, argc, script); /* create table 'arg' */ | 635 | createargtable(L, argv, argc, script); /* create table 'arg' */ |
| 636 | lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */ | 636 | lua_gc(L, LUA_GCRESTART); /* start GC... */ |
| 637 | lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */ | ||
| 637 | if (!(args & has_E)) { /* no option '-E'? */ | 638 | if (!(args & has_E)) { /* no option '-E'? */ |
| 638 | if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ | 639 | if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ |
| 639 | return 0; /* error running LUA_INIT */ | 640 | return 0; /* error running LUA_INIT */ |
| @@ -665,6 +666,7 @@ int main (int argc, char **argv) { | |||
| 665 | l_message(argv[0], "cannot create state: not enough memory"); | 666 | l_message(argv[0], "cannot create state: not enough memory"); |
| 666 | return EXIT_FAILURE; | 667 | return EXIT_FAILURE; |
| 667 | } | 668 | } |
| 669 | lua_gc(L, LUA_GCSTOP); /* stop GC while buidling state */ | ||
| 668 | lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ | 670 | lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ |
| 669 | lua_pushinteger(L, argc); /* 1st argument */ | 671 | lua_pushinteger(L, argc); /* 1st argument */ |
| 670 | lua_pushlightuserdata(L, argv); /* 2nd argument */ | 672 | lua_pushlightuserdata(L, argv); /* 2nd argument */ |
