diff options
Diffstat (limited to 'lgc.c')
-rw-r--r-- | lgc.c | 253 |
1 files changed, 121 insertions, 132 deletions
@@ -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 | ||