diff options
Diffstat (limited to 'lgc.c')
-rw-r--r-- | lgc.c | 278 |
1 files changed, 132 insertions, 146 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lgc.c,v 2.227 2017/04/30 20:43:26 roberto Exp roberto $ | 2 | ** $Id: lgc.c,v 2.228 2017/05/04 13:32:01 roberto Exp roberto $ |
3 | ** Garbage Collector | 3 | ** Garbage Collector |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -27,23 +27,29 @@ | |||
27 | 27 | ||
28 | 28 | ||
29 | /* | 29 | /* |
30 | ** cost of sweeping one element (the size of a small object divided | 30 | ** Maximum number of elements to sweep in each single step. |
31 | ** by some adjust for the sweep speed) | 31 | ** (Large enough to dissipate fixed overheads but small enough |
32 | ** to allow small steps for the collector.) | ||
32 | */ | 33 | */ |
33 | #define GCSWEEPCOST ((sizeof(TString) + 4) / 4) | 34 | #define GCSWEEPMAX 100 |
35 | |||
36 | /* | ||
37 | ** Maximum number of finalizers to call in each single step. | ||
38 | */ | ||
39 | #define GCFINMAX 10 | ||
34 | 40 | ||
35 | /* maximum number of elements to sweep in each single step */ | ||
36 | #define GCSWEEPMAX (cast_int((GCSTEPSIZE / GCSWEEPCOST) / 4)) | ||
37 | 41 | ||
38 | /* cost of calling one finalizer */ | 42 | /* |
39 | #define GCFINALIZECOST GCSWEEPCOST | 43 | ** Cost of calling one finalizer. |
44 | */ | ||
45 | #define GCFINALIZECOST 50 | ||
40 | 46 | ||
41 | 47 | ||
42 | /* | 48 | /* |
43 | ** macro to adjust 'stepmul': 'stepmul' is actually used like | 49 | ** The equivalent, in bytes, of one unit of "work" (visiting a slot, |
44 | ** 'stepmul / STEPMULADJ' (value chosen by tests) | 50 | ** sweeping an object, etc.) * 10 (for scaling) |
45 | */ | 51 | */ |
46 | #define STEPMULADJ 200 | 52 | #define WORK2MEM (sizeof(TValue) * 10) |
47 | 53 | ||
48 | 54 | ||
49 | /* | 55 | /* |
@@ -86,7 +92,7 @@ | |||
86 | #define markobjectN(g,t) { if (t) markobject(g,t); } | 92 | #define markobjectN(g,t) { if (t) markobject(g,t); } |
87 | 93 | ||
88 | static void reallymarkobject (global_State *g, GCObject *o); | 94 | static void reallymarkobject (global_State *g, GCObject *o); |
89 | static l_mem atomic (lua_State *L); | 95 | static lu_mem atomic (lua_State *L); |
90 | 96 | ||
91 | 97 | ||
92 | /* | 98 | /* |
@@ -246,21 +252,15 @@ static void reallymarkobject (global_State *g, GCObject *o) { | |||
246 | reentry: | 252 | reentry: |
247 | white2gray(o); | 253 | white2gray(o); |
248 | switch (o->tt) { | 254 | switch (o->tt) { |
249 | case LUA_TSHRSTR: { | 255 | case LUA_TSHRSTR: |
250 | gray2black(o); | ||
251 | g->GCmemtrav += sizelstring(gco2ts(o)->shrlen); | ||
252 | break; | ||
253 | } | ||
254 | case LUA_TLNGSTR: { | 256 | case LUA_TLNGSTR: { |
255 | gray2black(o); | 257 | gray2black(o); |
256 | g->GCmemtrav += sizelstring(gco2ts(o)->u.lnglen); | ||
257 | break; | 258 | break; |
258 | } | 259 | } |
259 | case LUA_TUSERDATA: { | 260 | case LUA_TUSERDATA: { |
260 | TValue uvalue; | 261 | TValue uvalue; |
261 | markobjectN(g, gco2u(o)->metatable); /* mark its metatable */ | 262 | markobjectN(g, gco2u(o)->metatable); /* mark its metatable */ |
262 | gray2black(o); | 263 | gray2black(o); |
263 | g->GCmemtrav += sizeudata(gco2u(o)); | ||
264 | getuservalue(g->mainthread, gco2u(o), &uvalue); | 264 | getuservalue(g->mainthread, gco2u(o), &uvalue); |
265 | if (valiswhite(&uvalue)) { /* markvalue(g, &uvalue); */ | 265 | if (valiswhite(&uvalue)) { /* markvalue(g, &uvalue); */ |
266 | o = gcvalue(&uvalue); | 266 | o = gcvalue(&uvalue); |
@@ -270,7 +270,6 @@ static void reallymarkobject (global_State *g, GCObject *o) { | |||
270 | } | 270 | } |
271 | case LUA_TUPVAL: { | 271 | case LUA_TUPVAL: { |
272 | UpVal *uv = gco2upv(o); | 272 | UpVal *uv = gco2upv(o); |
273 | g->GCmemtrav += sizeof(UpVal); | ||
274 | if (!upisopen(uv)) /* open upvalues are kept gray */ | 273 | if (!upisopen(uv)) /* open upvalues are kept gray */ |
275 | gray2black(o); | 274 | gray2black(o); |
276 | markvalue(g, uv->v); /* mark its content */ | 275 | markvalue(g, uv->v); /* mark its content */ |
@@ -314,10 +313,14 @@ static void markmt (global_State *g) { | |||
314 | /* | 313 | /* |
315 | ** mark all objects in list of being-finalized | 314 | ** mark all objects in list of being-finalized |
316 | */ | 315 | */ |
317 | static void markbeingfnz (global_State *g) { | 316 | static lu_mem markbeingfnz (global_State *g) { |
318 | GCObject *o; | 317 | GCObject *o; |
319 | for (o = g->tobefnz; o != NULL; o = o->next) | 318 | lu_mem count = 0; |
319 | for (o = g->tobefnz; o != NULL; o = o->next) { | ||
320 | count++; | ||
320 | markobject(g, o); | 321 | markobject(g, o); |
322 | } | ||
323 | return count; | ||
321 | } | 324 | } |
322 | 325 | ||
323 | 326 | ||
@@ -327,10 +330,12 @@ static void markbeingfnz (global_State *g) { | |||
327 | ** thread.) Remove from the list threads that no longer have upvalues and | 330 | ** thread.) Remove from the list threads that no longer have upvalues and |
328 | ** not-marked threads. | 331 | ** not-marked threads. |
329 | */ | 332 | */ |
330 | static void remarkupvals (global_State *g) { | 333 | static int remarkupvals (global_State *g) { |
331 | lua_State *thread; | 334 | lua_State *thread; |
332 | lua_State **p = &g->twups; | 335 | lua_State **p = &g->twups; |
336 | int work = 0; | ||
333 | while ((thread = *p) != NULL) { | 337 | while ((thread = *p) != NULL) { |
338 | work++; | ||
334 | lua_assert(!isblack(thread)); /* threads are never black */ | 339 | lua_assert(!isblack(thread)); /* threads are never black */ |
335 | if (isgray(thread) && thread->openupval != NULL) | 340 | if (isgray(thread) && thread->openupval != NULL) |
336 | p = &thread->twups; /* keep marked thread with upvalues in the list */ | 341 | p = &thread->twups; /* keep marked thread with upvalues in the list */ |
@@ -339,11 +344,13 @@ static void remarkupvals (global_State *g) { | |||
339 | *p = thread->twups; /* remove thread from the list */ | 344 | *p = thread->twups; /* remove thread from the list */ |
340 | thread->twups = thread; /* mark that it is out of list */ | 345 | thread->twups = thread; /* mark that it is out of list */ |
341 | for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { | 346 | for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { |
347 | work++; | ||
342 | if (!iswhite(uv)) /* upvalue already visited? */ | 348 | if (!iswhite(uv)) /* upvalue already visited? */ |
343 | markvalue(g, uv->v); /* mark its value */ | 349 | markvalue(g, uv->v); /* mark its value */ |
344 | } | 350 | } |
345 | } | 351 | } |
346 | } | 352 | } |
353 | return work; | ||
347 | } | 354 | } |
348 | 355 | ||
349 | 356 | ||
@@ -491,8 +498,7 @@ static lu_mem traversetable (global_State *g, Table *h) { | |||
491 | } | 498 | } |
492 | else /* not weak */ | 499 | else /* not weak */ |
493 | traversestrongtable(g, h); | 500 | traversestrongtable(g, h); |
494 | return sizeof(Table) + sizeof(TValue) * h->sizearray + | 501 | return 1 + h->sizearray + 2 * allocsizenode(h); |
495 | sizeof(Node) * cast(size_t, allocsizenode(h)); | ||
496 | } | 502 | } |
497 | 503 | ||
498 | 504 | ||
@@ -539,38 +545,33 @@ static int traverseproto (global_State *g, Proto *f) { | |||
539 | markobjectN(g, f->p[i]); | 545 | markobjectN(g, f->p[i]); |
540 | for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ | 546 | for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ |
541 | markobjectN(g, f->locvars[i].varname); | 547 | markobjectN(g, f->locvars[i].varname); |
542 | return sizeof(Proto) + sizeof(Instruction) * f->sizecode + | 548 | return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; |
543 | sizeof(Proto *) * f->sizep + | ||
544 | sizeof(TValue) * f->sizek + | ||
545 | sizeof(int) * f->sizelineinfo + | ||
546 | sizeof(LocVar) * f->sizelocvars + | ||
547 | sizeof(Upvaldesc) * f->sizeupvalues; | ||
548 | } | 549 | } |
549 | 550 | ||
550 | 551 | ||
551 | static lu_mem traverseCclosure (global_State *g, CClosure *cl) { | 552 | static int traverseCclosure (global_State *g, CClosure *cl) { |
552 | int i; | 553 | int i; |
553 | for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ | 554 | for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ |
554 | markvalue(g, &cl->upvalue[i]); | 555 | markvalue(g, &cl->upvalue[i]); |
555 | return sizeCclosure(cl->nupvalues); | 556 | return 1 + cl->nupvalues; |
556 | } | 557 | } |
557 | 558 | ||
558 | /* | 559 | /* |
559 | ** Traverse a Lua closure, marking its prototype and its upvalues. | 560 | ** Traverse a Lua closure, marking its prototype and its upvalues. |
560 | ** (Both can be NULL while closure is being created.) | 561 | ** (Both can be NULL while closure is being created.) |
561 | */ | 562 | */ |
562 | static lu_mem traverseLclosure (global_State *g, LClosure *cl) { | 563 | static int traverseLclosure (global_State *g, LClosure *cl) { |
563 | int i; | 564 | int i; |
564 | markobjectN(g, cl->p); /* mark its prototype */ | 565 | markobjectN(g, cl->p); /* mark its prototype */ |
565 | for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ | 566 | for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ |
566 | UpVal *uv = cl->upvals[i]; | 567 | UpVal *uv = cl->upvals[i]; |
567 | markobjectN(g, uv); /* mark upvalue */ | 568 | markobjectN(g, uv); /* mark upvalue */ |
568 | } | 569 | } |
569 | return sizeLclosure(cl->nupvalues); | 570 | return 1 + cl->nupvalues; |
570 | } | 571 | } |
571 | 572 | ||
572 | 573 | ||
573 | static lu_mem traversethread (global_State *g, lua_State *th) { | 574 | static int traversethread (global_State *g, lua_State *th) { |
574 | StkId o = th->stack; | 575 | StkId o = th->stack; |
575 | if (o == NULL) | 576 | if (o == NULL) |
576 | return 1; /* stack not completely built yet */ | 577 | return 1; /* stack not completely built yet */ |
@@ -590,8 +591,7 @@ static lu_mem traversethread (global_State *g, lua_State *th) { | |||
590 | } | 591 | } |
591 | else if (!g->gcemergency) | 592 | else if (!g->gcemergency) |
592 | luaD_shrinkstack(th); /* do not change stack in emergency cycle */ | 593 | luaD_shrinkstack(th); /* do not change stack in emergency cycle */ |
593 | return (sizeof(lua_State) + sizeof(TValue) * th->stacksize + | 594 | return 1 + th->stacksize; |
594 | sizeof(CallInfo) * th->nci); | ||
595 | } | 595 | } |
596 | 596 | ||
597 | 597 | ||
@@ -599,51 +599,47 @@ static lu_mem traversethread (global_State *g, lua_State *th) { | |||
599 | ** traverse one gray object, turning it to black (except for threads, | 599 | ** traverse one gray object, turning it to black (except for threads, |
600 | ** which are always gray). | 600 | ** which are always gray). |
601 | */ | 601 | */ |
602 | static void propagatemark (global_State *g) { | 602 | static lu_mem propagatemark (global_State *g) { |
603 | lu_mem size; | ||
604 | GCObject *o = g->gray; | 603 | GCObject *o = g->gray; |
605 | gray2black(o); | 604 | gray2black(o); |
606 | switch (o->tt) { | 605 | switch (o->tt) { |
607 | case LUA_TTABLE: { | 606 | case LUA_TTABLE: { |
608 | Table *h = gco2t(o); | 607 | Table *h = gco2t(o); |
609 | g->gray = h->gclist; /* remove from 'gray' list */ | 608 | g->gray = h->gclist; /* remove from 'gray' list */ |
610 | size = traversetable(g, h); | 609 | return traversetable(g, h); |
611 | break; | ||
612 | } | 610 | } |
613 | case LUA_TLCL: { | 611 | case LUA_TLCL: { |
614 | LClosure *cl = gco2lcl(o); | 612 | LClosure *cl = gco2lcl(o); |
615 | g->gray = cl->gclist; /* remove from 'gray' list */ | 613 | g->gray = cl->gclist; /* remove from 'gray' list */ |
616 | size = traverseLclosure(g, cl); | 614 | return traverseLclosure(g, cl); |
617 | break; | ||
618 | } | 615 | } |
619 | case LUA_TCCL: { | 616 | case LUA_TCCL: { |
620 | CClosure *cl = gco2ccl(o); | 617 | CClosure *cl = gco2ccl(o); |
621 | g->gray = cl->gclist; /* remove from 'gray' list */ | 618 | g->gray = cl->gclist; /* remove from 'gray' list */ |
622 | size = traverseCclosure(g, cl); | 619 | return traverseCclosure(g, cl); |
623 | break; | ||
624 | } | 620 | } |
625 | case LUA_TTHREAD: { | 621 | case LUA_TTHREAD: { |
626 | lua_State *th = gco2th(o); | 622 | lua_State *th = gco2th(o); |
627 | g->gray = th->gclist; /* remove from 'gray' list */ | 623 | g->gray = th->gclist; /* remove from 'gray' list */ |
628 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ | 624 | linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ |
629 | black2gray(o); | 625 | black2gray(o); |
630 | size = traversethread(g, th); | 626 | return traversethread(g, th); |
631 | break; | ||
632 | } | 627 | } |
633 | case LUA_TPROTO: { | 628 | case LUA_TPROTO: { |
634 | Proto *p = gco2p(o); | 629 | Proto *p = gco2p(o); |
635 | g->gray = p->gclist; /* remove from 'gray' list */ | 630 | g->gray = p->gclist; /* remove from 'gray' list */ |
636 | size = traverseproto(g, p); | 631 | return traverseproto(g, p); |
637 | break; | ||
638 | } | 632 | } |
639 | default: lua_assert(0); return; | 633 | default: lua_assert(0); return 0; |
640 | } | 634 | } |
641 | g->GCmemtrav += size; | ||
642 | } | 635 | } |
643 | 636 | ||
644 | 637 | ||
645 | static void propagateall (global_State *g) { | 638 | static lu_mem propagateall (global_State *g) { |
646 | while (g->gray) propagatemark(g); | 639 | lu_mem tot = 0; |
640 | while (g->gray) | ||
641 | tot += propagatemark(g); | ||
642 | return tot; | ||
647 | } | 643 | } |
648 | 644 | ||
649 | 645 | ||
@@ -719,7 +715,7 @@ static void clearvalues (global_State *g, GCObject *l, GCObject *f) { | |||
719 | setnilvalue(o); /* remove value */ | 715 | setnilvalue(o); /* remove value */ |
720 | } | 716 | } |
721 | for (n = gnode(h, 0); n < limit; n++) { | 717 | for (n = gnode(h, 0); n < limit; n++) { |
722 | if (!ttisnil(gval(n)) && iscleared(g, gval(n))) { | 718 | if (iscleared(g, gval(n))) { |
723 | setnilvalue(gval(n)); /* remove value ... */ | 719 | setnilvalue(gval(n)); /* remove value ... */ |
724 | removeentry(n); /* and remove entry from table */ | 720 | removeentry(n); /* and remove entry from table */ |
725 | } | 721 | } |
@@ -770,22 +766,20 @@ static void freeobj (lua_State *L, GCObject *o) { | |||
770 | } | 766 | } |
771 | 767 | ||
772 | 768 | ||
773 | #define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) | ||
774 | static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count); | ||
775 | |||
776 | |||
777 | /* | 769 | /* |
778 | ** sweep at most 'count' elements from a list of GCObjects erasing dead | 770 | ** sweep at most 'countin' elements from a list of GCObjects erasing dead |
779 | ** objects, where a dead object is one marked with the old (non current) | 771 | ** objects, where a dead object is one marked with the old (non current) |
780 | ** white; change all non-dead objects back to white, preparing for next | 772 | ** white; change all non-dead objects back to white, preparing for next |
781 | ** collection cycle. Return where to continue the traversal or NULL if | 773 | ** collection cycle. Return where to continue the traversal or NULL if |
782 | ** list is finished. | 774 | ** list is finished. ('*countout' gets the number of elements traversed.) |
783 | */ | 775 | */ |
784 | static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { | 776 | static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, |
777 | int *countout) { | ||
785 | global_State *g = G(L); | 778 | global_State *g = G(L); |
786 | int ow = otherwhite(g); | 779 | int ow = otherwhite(g); |
780 | int i; | ||
787 | int white = luaC_white(g); /* current white */ | 781 | int white = luaC_white(g); /* current white */ |
788 | while (*p != NULL && count-- > 0) { | 782 | for (i = 0; *p != NULL && i < countin; i++) { |
789 | GCObject *curr = *p; | 783 | GCObject *curr = *p; |
790 | int marked = curr->marked; | 784 | int marked = curr->marked; |
791 | if (isdeadm(ow, marked)) { /* is 'curr' dead? */ | 785 | if (isdeadm(ow, marked)) { /* is 'curr' dead? */ |
@@ -797,6 +791,8 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { | |||
797 | p = &curr->next; /* go to next element */ | 791 | p = &curr->next; /* go to next element */ |
798 | } | 792 | } |
799 | } | 793 | } |
794 | if (countout) | ||
795 | *countout = i; /* number of elements traversed */ | ||
800 | return (*p == NULL) ? NULL : p; | 796 | return (*p == NULL) ? NULL : p; |
801 | } | 797 | } |
802 | 798 | ||
@@ -807,7 +803,7 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { | |||
807 | static GCObject **sweeptolive (lua_State *L, GCObject **p) { | 803 | static GCObject **sweeptolive (lua_State *L, GCObject **p) { |
808 | GCObject **old = p; | 804 | GCObject **old = p; |
809 | do { | 805 | do { |
810 | p = sweeplist(L, p, 1); | 806 | p = sweeplist(L, p, 1, NULL); |
811 | } while (p == old); | 807 | } while (p == old); |
812 | return p; | 808 | return p; |
813 | } | 809 | } |
@@ -892,16 +888,13 @@ static void GCTM (lua_State *L, int propagateerrors) { | |||
892 | 888 | ||
893 | 889 | ||
894 | /* | 890 | /* |
895 | ** call a few (up to 'g->gcfinnum') finalizers | 891 | ** Call a few finalizers |
896 | */ | 892 | */ |
897 | static int runafewfinalizers (lua_State *L) { | 893 | static int runafewfinalizers (lua_State *L, int n) { |
898 | global_State *g = G(L); | 894 | global_State *g = G(L); |
899 | unsigned int i; | 895 | int i; |
900 | lua_assert(!g->tobefnz || g->gcfinnum > 0); | 896 | for (i = 0; i < n && g->tobefnz; i++) |
901 | for (i = 0; g->tobefnz && i < g->gcfinnum; i++) | ||
902 | GCTM(L, 1); /* call one finalizer */ | 897 | GCTM(L, 1); /* call one finalizer */ |
903 | g->gcfinnum = (!g->tobefnz) ? 0 /* nothing more to finalize? */ | ||
904 | : g->gcfinnum * 2; /* else call a few more next time */ | ||
905 | return i; | 898 | return i; |
906 | } | 899 | } |
907 | 900 | ||
@@ -1285,9 +1278,6 @@ static void genstep (lua_State *L, global_State *g) { | |||
1285 | //lua_checkmemory(L); | 1278 | //lua_checkmemory(L); |
1286 | } | 1279 | } |
1287 | 1280 | ||
1288 | |||
1289 | |||
1290 | |||
1291 | /* }====================================================== */ | 1281 | /* }====================================================== */ |
1292 | 1282 | ||
1293 | 1283 | ||
@@ -1299,26 +1289,28 @@ static void genstep (lua_State *L, global_State *g) { | |||
1299 | 1289 | ||
1300 | 1290 | ||
1301 | /* | 1291 | /* |
1302 | ** Set a reasonable "time" to wait before starting a new GC cycle; cycle | 1292 | ** Set the "time" to wait before starting a new GC cycle; cycle will |
1303 | ** will start when memory use hits threshold. (Division by 'estimate' | 1293 | ** start when memory use hits the threshold of ('estimate' * gcpause / |
1304 | ** should be OK: it cannot be zero (because Lua cannot even start with | 1294 | ** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, |
1305 | ** less than PAUSEADJ bytes). | 1295 | ** because Lua cannot even start with less than PAUSEADJ bytes). |
1306 | */ | 1296 | */ |
1307 | static void setpause (global_State *g) { | 1297 | static void setpause (global_State *g) { |
1308 | l_mem threshold, debt; | 1298 | l_mem threshold, debt; |
1299 | int pause = g->gcpause + 100; | ||
1309 | l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ | 1300 | l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ |
1310 | lua_assert(estimate > 0); | 1301 | lua_assert(estimate > 0); |
1311 | threshold = (g->gcpause < MAX_LMEM / estimate) /* overflow? */ | 1302 | threshold = (pause < MAX_LMEM / estimate) /* overflow? */ |
1312 | ? estimate * g->gcpause /* no overflow */ | 1303 | ? estimate * pause /* no overflow */ |
1313 | : MAX_LMEM; /* overflow; truncate to maximum */ | 1304 | : MAX_LMEM; /* overflow; truncate to maximum */ |
1314 | debt = gettotalbytes(g) - threshold; | 1305 | debt = gettotalbytes(g) - threshold; |
1306 | if (debt > 0) debt = 0; | ||
1315 | luaE_setdebt(g, debt); | 1307 | luaE_setdebt(g, debt); |
1316 | } | 1308 | } |
1317 | 1309 | ||
1318 | 1310 | ||
1319 | /* | 1311 | /* |
1320 | ** Enter first sweep phase. | 1312 | ** Enter first sweep phase. |
1321 | ** The call to 'sweeplist' tries to make pointer point to an object | 1313 | ** The call to 'sweeptolive' makes the pointer point to an object |
1322 | ** inside the list (instead of to the header), so that the real sweep do | 1314 | ** inside the list (instead of to the header), so that the real sweep do |
1323 | ** not need to skip objects created between "now" and the start of the | 1315 | ** not need to skip objects created between "now" and the start of the |
1324 | ** real sweep. | 1316 | ** real sweep. |
@@ -1327,11 +1319,15 @@ static void entersweep (lua_State *L) { | |||
1327 | global_State *g = G(L); | 1319 | global_State *g = G(L); |
1328 | g->gcstate = GCSswpallgc; | 1320 | g->gcstate = GCSswpallgc; |
1329 | lua_assert(g->sweepgc == NULL); | 1321 | lua_assert(g->sweepgc == NULL); |
1330 | g->sweepgc = sweeplist(L, &g->allgc, 1); | 1322 | g->sweepgc = sweeptolive(L, &g->allgc); |
1331 | } | 1323 | } |
1332 | 1324 | ||
1333 | 1325 | ||
1334 | static void deletealllist (lua_State *L, GCObject *p, GCObject *limit) { | 1326 | /* |
1327 | ** Delete all objects in list 'p' until (but not including) object | ||
1328 | ** 'limit'. | ||
1329 | */ | ||
1330 | static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { | ||
1335 | while (p != limit) { | 1331 | while (p != limit) { |
1336 | GCObject *next = p->next; | 1332 | GCObject *next = p->next; |
1337 | freeobj(L, p); | 1333 | freeobj(L, p); |
@@ -1340,52 +1336,50 @@ static void deletealllist (lua_State *L, GCObject *p, GCObject *limit) { | |||
1340 | } | 1336 | } |
1341 | 1337 | ||
1342 | 1338 | ||
1339 | /* | ||
1340 | ** Call all finalizers of the objects in the given Lua state, and | ||
1341 | ** then free all objects, except for the main thread. | ||
1342 | */ | ||
1343 | void luaC_freeallobjects (lua_State *L) { | 1343 | void luaC_freeallobjects (lua_State *L) { |
1344 | global_State *g = G(L); | 1344 | global_State *g = G(L); |
1345 | luaC_changemode(L, KGC_INC); | 1345 | luaC_changemode(L, KGC_INC); |
1346 | separatetobefnz(g, 1); /* separate all objects with finalizers */ | 1346 | separatetobefnz(g, 1); /* separate all objects with finalizers */ |
1347 | lua_assert(g->finobj == NULL); | 1347 | lua_assert(g->finobj == NULL); |
1348 | callallpendingfinalizers(L); | 1348 | callallpendingfinalizers(L); |
1349 | deletealllist(L, g->allgc, obj2gco(g->mainthread)); | 1349 | deletelist(L, g->allgc, obj2gco(g->mainthread)); |
1350 | deletealllist(L, g->finobj, NULL); | 1350 | deletelist(L, g->finobj, NULL); |
1351 | deletealllist(L, g->fixedgc, NULL); /* collect fixed objects */ | 1351 | deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ |
1352 | lua_assert(g->strt.nuse == 0); | 1352 | lua_assert(g->strt.nuse == 0); |
1353 | } | 1353 | } |
1354 | 1354 | ||
1355 | 1355 | ||
1356 | static l_mem atomic (lua_State *L) { | 1356 | static lu_mem atomic (lua_State *L) { |
1357 | global_State *g = G(L); | 1357 | global_State *g = G(L); |
1358 | l_mem work; | 1358 | lu_mem work = 0; |
1359 | GCObject *origweak, *origall; | 1359 | GCObject *origweak, *origall; |
1360 | GCObject *grayagain = g->grayagain; /* save original list */ | 1360 | GCObject *grayagain = g->grayagain; /* save original list */ |
1361 | g->grayagain = NULL; | 1361 | g->grayagain = NULL; |
1362 | lua_assert(g->ephemeron == NULL && g->weak == NULL); | 1362 | lua_assert(g->ephemeron == NULL && g->weak == NULL); |
1363 | lua_assert(!iswhite(g->mainthread)); | 1363 | lua_assert(!iswhite(g->mainthread)); |
1364 | g->gcstate = GCSatomic; | 1364 | g->gcstate = GCSatomic; |
1365 | g->GCmemtrav = 0; /* start counting work */ | ||
1366 | markobject(g, L); /* mark running thread */ | 1365 | markobject(g, L); /* mark running thread */ |
1367 | /* registry and global metatables may be changed by API */ | 1366 | /* registry and global metatables may be changed by API */ |
1368 | markvalue(g, &g->l_registry); | 1367 | markvalue(g, &g->l_registry); |
1369 | markmt(g); /* mark global metatables */ | 1368 | markmt(g); /* mark global metatables */ |
1370 | /* remark occasional upvalues of (maybe) dead threads */ | 1369 | /* remark occasional upvalues of (maybe) dead threads */ |
1371 | remarkupvals(g); | 1370 | work += remarkupvals(g); |
1372 | propagateall(g); /* propagate changes */ | 1371 | work += propagateall(g); /* propagate changes */ |
1373 | work = g->GCmemtrav; /* stop counting (do not recount 'grayagain') */ | ||
1374 | g->gray = grayagain; | 1372 | g->gray = grayagain; |
1375 | propagateall(g); /* traverse 'grayagain' list */ | 1373 | work += propagateall(g); /* traverse 'grayagain' list */ |
1376 | g->GCmemtrav = 0; /* restart counting */ | ||
1377 | convergeephemerons(g); | 1374 | convergeephemerons(g); |
1378 | /* at this point, all strongly accessible objects are marked. */ | 1375 | /* at this point, all strongly accessible objects are marked. */ |
1379 | /* Clear values from weak tables, before checking finalizers */ | 1376 | /* Clear values from weak tables, before checking finalizers */ |
1380 | clearvalues(g, g->weak, NULL); | 1377 | clearvalues(g, g->weak, NULL); |
1381 | clearvalues(g, g->allweak, NULL); | 1378 | clearvalues(g, g->allweak, NULL); |
1382 | origweak = g->weak; origall = g->allweak; | 1379 | origweak = g->weak; origall = g->allweak; |
1383 | work += g->GCmemtrav; /* stop counting (objects being finalized) */ | ||
1384 | separatetobefnz(g, 0); /* separate objects to be finalized */ | 1380 | separatetobefnz(g, 0); /* separate objects to be finalized */ |
1385 | g->gcfinnum = 1; /* there may be objects to be finalized */ | 1381 | work += markbeingfnz(g); /* mark objects that will be finalized */ |
1386 | markbeingfnz(g); /* mark objects that will be finalized */ | 1382 | work += propagateall(g); /* remark, to propagate 'resurrection' */ |
1387 | propagateall(g); /* remark, to propagate 'resurrection' */ | ||
1388 | g->GCmemtrav = 0; /* restart counting */ | ||
1389 | convergeephemerons(g); | 1383 | convergeephemerons(g); |
1390 | /* at this point, all resurrected objects are marked. */ | 1384 | /* at this point, all resurrected objects are marked. */ |
1391 | /* remove dead objects from weak tables */ | 1385 | /* remove dead objects from weak tables */ |
@@ -1398,24 +1392,24 @@ static l_mem atomic (lua_State *L) { | |||
1398 | clearprotolist(g); | 1392 | clearprotolist(g); |
1399 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ | 1393 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ |
1400 | lua_assert(g->gray == NULL); | 1394 | lua_assert(g->gray == NULL); |
1401 | work += g->GCmemtrav; /* complete counting */ | 1395 | return work; /* estimate of slots marked by 'atomic' */ |
1402 | return work; /* estimate of memory marked by 'atomic' */ | ||
1403 | } | 1396 | } |
1404 | 1397 | ||
1405 | 1398 | ||
1406 | static lu_mem sweepstep (lua_State *L, global_State *g, | 1399 | static int sweepstep (lua_State *L, global_State *g, |
1407 | int nextstate, GCObject **nextlist) { | 1400 | int nextstate, GCObject **nextlist) { |
1408 | if (g->sweepgc) { | 1401 | if (g->sweepgc) { |
1409 | l_mem olddebt = g->GCdebt; | 1402 | l_mem olddebt = g->GCdebt; |
1410 | g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); | 1403 | int count; |
1404 | g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count); | ||
1411 | g->GCestimate += g->GCdebt - olddebt; /* update estimate */ | 1405 | g->GCestimate += g->GCdebt - olddebt; /* update estimate */ |
1412 | if (g->sweepgc) /* is there still something to sweep? */ | 1406 | return count; |
1413 | return (GCSWEEPMAX * GCSWEEPCOST); | 1407 | } |
1408 | else { /* enter next state */ | ||
1409 | g->gcstate = nextstate; | ||
1410 | g->sweepgc = nextlist; | ||
1411 | return 0; /* no work done */ | ||
1414 | } | 1412 | } |
1415 | /* else enter next state */ | ||
1416 | g->gcstate = nextstate; | ||
1417 | g->sweepgc = nextlist; | ||
1418 | return 0; | ||
1419 | } | 1413 | } |
1420 | 1414 | ||
1421 | 1415 | ||
@@ -1423,23 +1417,21 @@ static lu_mem singlestep (lua_State *L) { | |||
1423 | global_State *g = G(L); | 1417 | global_State *g = G(L); |
1424 | switch (g->gcstate) { | 1418 | switch (g->gcstate) { |
1425 | case GCSpause: { | 1419 | case GCSpause: { |
1426 | g->GCmemtrav = g->strt.size * sizeof(GCObject*); | ||
1427 | restartcollection(g); | 1420 | restartcollection(g); |
1428 | g->gcstate = GCSpropagate; | 1421 | g->gcstate = GCSpropagate; |
1429 | return g->GCmemtrav; | 1422 | return 1; |
1430 | } | 1423 | } |
1431 | case GCSpropagate: { | 1424 | case GCSpropagate: { |
1432 | g->GCmemtrav = 0; | 1425 | if (g->gray == NULL) { /* no more gray objects? */ |
1433 | if (g->gray == NULL) /* no more gray objects? */ | ||
1434 | g->gcstate = GCSenteratomic; /* finish propagate phase */ | 1426 | g->gcstate = GCSenteratomic; /* finish propagate phase */ |
1427 | return 0; | ||
1428 | } | ||
1435 | else | 1429 | else |
1436 | propagatemark(g); /* traverse one gray object */ | 1430 | return propagatemark(g); /* traverse one gray object */ |
1437 | return g->GCmemtrav; /* memory traversed in this step */ | ||
1438 | } | 1431 | } |
1439 | case GCSenteratomic: { | 1432 | case GCSenteratomic: { |
1440 | lu_mem work; | 1433 | lu_mem work = propagateall(g); /* make sure gray list is empty */ |
1441 | propagateall(g); /* make sure gray list is empty */ | 1434 | work += atomic(L); /* work is what was traversed by 'atomic' */ |
1442 | work = atomic(L); /* work is what was traversed by 'atomic' */ | ||
1443 | entersweep(L); | 1435 | entersweep(L); |
1444 | g->GCestimate = gettotalbytes(g); /* first estimate */; | 1436 | g->GCestimate = gettotalbytes(g); /* first estimate */; |
1445 | return work; | 1437 | return work; |
@@ -1460,8 +1452,8 @@ static lu_mem singlestep (lua_State *L) { | |||
1460 | } | 1452 | } |
1461 | case GCScallfin: { /* call remaining finalizers */ | 1453 | case GCScallfin: { /* call remaining finalizers */ |
1462 | if (g->tobefnz && !g->gcemergency) { | 1454 | if (g->tobefnz && !g->gcemergency) { |
1463 | int n = runafewfinalizers(L); | 1455 | int n = runafewfinalizers(L, GCFINMAX); |
1464 | return (n * GCFINALIZECOST); | 1456 | return n * GCFINALIZECOST; |
1465 | } | 1457 | } |
1466 | else { /* emergency mode or no more finalizers */ | 1458 | else { /* emergency mode or no more finalizers */ |
1467 | g->gcstate = GCSpause; /* finish collection */ | 1459 | g->gcstate = GCSpause; /* finish collection */ |
@@ -1485,45 +1477,36 @@ void luaC_runtilstate (lua_State *L, int statesmask) { | |||
1485 | 1477 | ||
1486 | 1478 | ||
1487 | /* | 1479 | /* |
1488 | ** get GC debt and convert it from Kb to 'work units' (avoid zero debt | 1480 | ** Performs a basic incremental step. The debt and step size are |
1489 | ** and overflows) | 1481 | ** converted from bytes to "units of work"; then the function loops |
1490 | */ | 1482 | ** running single steps until adding that many units of work or |
1491 | static l_mem getdebt (global_State *g) { | 1483 | ** finishing a cycle (pause state). Finally, it sets the debt that |
1492 | l_mem debt = g->GCdebt; | 1484 | ** controls when next step will be performed. |
1493 | int stepmul = g->gcstepmul; | ||
1494 | if (debt <= 0) return 0; /* minimal debt */ | ||
1495 | else { | ||
1496 | debt = (debt / STEPMULADJ) + 1; | ||
1497 | debt = (debt < MAX_LMEM / stepmul) ? debt * stepmul : MAX_LMEM; | ||
1498 | return debt; | ||
1499 | } | ||
1500 | } | ||
1501 | |||
1502 | /* | ||
1503 | ** performs a basic incremental step | ||
1504 | */ | 1485 | */ |
1505 | static void incstep (lua_State *L, global_State *g) { | 1486 | static void incstep (lua_State *L, global_State *g) { |
1506 | l_mem debt = getdebt(g); /* GC deficit (to be paid now) */ | 1487 | int stepmul = (g->gcstepmul | 1); /* avoid division by 0 */ |
1488 | l_mem debt = (g->GCdebt / WORK2MEM) * stepmul; | ||
1489 | l_mem stepsize = cast(l_mem, 1) << g->gcstepsize; | ||
1490 | stepsize = -((stepsize / WORK2MEM) * stepmul); | ||
1507 | do { /* repeat until pause or enough "credit" (negative debt) */ | 1491 | do { /* repeat until pause or enough "credit" (negative debt) */ |
1508 | lu_mem work = singlestep(L); /* perform one single step */ | 1492 | lu_mem work = singlestep(L); /* perform one single step */ |
1509 | debt -= work; | 1493 | debt -= work; |
1510 | } while (debt > -GCSTEPSIZE && g->gcstate != GCSpause); | 1494 | } while (debt > stepsize && g->gcstate != GCSpause); |
1511 | if (g->gcstate == GCSpause) | 1495 | if (g->gcstate == GCSpause) |
1512 | setpause(g); /* pause until next cycle */ | 1496 | setpause(g); /* pause until next cycle */ |
1513 | else { | 1497 | else { |
1514 | debt = (debt / g->gcstepmul) * STEPMULADJ; /* convert 'work units' to Kb */ | 1498 | debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */ |
1515 | luaE_setdebt(g, debt); | 1499 | luaE_setdebt(g, debt); |
1516 | runafewfinalizers(L); | ||
1517 | } | 1500 | } |
1518 | } | 1501 | } |
1519 | 1502 | ||
1520 | /* | 1503 | /* |
1521 | ** performs a basic GC step when collector is running | 1504 | ** performs a basic GC step if collector is running |
1522 | */ | 1505 | */ |
1523 | void luaC_step (lua_State *L) { | 1506 | void luaC_step (lua_State *L) { |
1524 | global_State *g = G(L); | 1507 | global_State *g = G(L); |
1525 | if (!g->gcrunning) /* not running? */ | 1508 | if (!g->gcrunning) /* not running? */ |
1526 | luaE_setdebt(g, -GCSTEPSIZE * 10); /* avoid being called too often */ | 1509 | luaE_setdebt(g, -MAX_LMEM); /* avoid being called without need */ |
1527 | else if (g->gckind == KGC_INC) | 1510 | else if (g->gckind == KGC_INC) |
1528 | incstep(L, g); | 1511 | incstep(L, g); |
1529 | else | 1512 | else |
@@ -1532,9 +1515,7 @@ void luaC_step (lua_State *L) { | |||
1532 | 1515 | ||
1533 | 1516 | ||
1534 | /* | 1517 | /* |
1535 | ** Performs a full GC cycle; if 'isemergency', set a flag to avoid | 1518 | ** Perform a full collection in incremental mode. |
1536 | ** some operations which could change the interpreter state in some | ||
1537 | ** unexpected ways (running finalizers and shrinking some structures). | ||
1538 | ** Before running the collection, check 'keepinvariant'; if it is true, | 1519 | ** Before running the collection, check 'keepinvariant'; if it is true, |
1539 | ** there may be some objects marked as black, so the collector has | 1520 | ** there may be some objects marked as black, so the collector has |
1540 | ** to sweep all objects to turn them back to white (as white has not | 1521 | ** to sweep all objects to turn them back to white (as white has not |
@@ -1553,6 +1534,11 @@ static void fullinc (lua_State *L, global_State *g) { | |||
1553 | } | 1534 | } |
1554 | 1535 | ||
1555 | 1536 | ||
1537 | /* | ||
1538 | ** Performs a full GC cycle; if 'isemergency', set a flag to avoid | ||
1539 | ** some operations which could change the interpreter state in some | ||
1540 | ** unexpected ways (running finalizers and shrinking some structures). | ||
1541 | */ | ||
1556 | void luaC_fullgc (lua_State *L, int isemergency) { | 1542 | void luaC_fullgc (lua_State *L, int isemergency) { |
1557 | global_State *g = G(L); | 1543 | global_State *g = G(L); |
1558 | lua_assert(!g->gcemergency); | 1544 | lua_assert(!g->gcemergency); |