kgc.c (27848B)
1 /* 2 ** kgc.c 3 ** Garbage Collector 4 ** See Copyright Notice in klisp.h 5 */ 6 7 /* 8 ** SOURCE NOTE: This is almost textually from lua. 9 ** Parts that don't apply, or don't apply yet to klisp are in comments. 10 */ 11 12 /* 13 ** LOCK: no locks are explicitly acquired here. 14 ** Whoever calls the GC needs to have already acquired the GIL. 15 */ 16 17 #include <string.h> 18 19 #include "kgc.h" 20 #include "kobject.h" 21 #include "kstate.h" 22 #include "kmem.h" 23 #include "kport.h" 24 #include "imath.h" 25 #include "imrat.h" 26 #include "ktable.h" 27 #include "kstring.h" 28 #include "kbytevector.h" 29 #include "kvector.h" 30 #include "kmutex.h" 31 #include "kcondvar.h" 32 #include "kerror.h" 33 34 #define GCSTEPSIZE 1024u 35 #define GCSWEEPMAX 40 36 #define GCSWEEPCOST 10 37 #define GCFINALIZECOST 100 /* klisp: NOT USED YET */ 38 39 40 41 #define maskmarks cast(uint16_t, ~(bitmask(BLACKBIT)|WHITEBITS)) 42 43 #define makewhite(g,x) \ 44 ((x)->gch.gct = cast(uint16_t, \ 45 ((x)->gch.gct & maskmarks) | klispC_white(g))) 46 47 #define white2gray(x) reset2bits((x)->gch.gct, WHITE0BIT, WHITE1BIT) 48 #define black2gray(x) resetbit((x)->gch.gct, BLACKBIT) 49 50 /* NOTE: klisp strings, unlike the lua counterparts are not values, 51 so they are marked as other objects */ 52 53 /* klisp: NOT USED YET */ 54 #define isfinalized(u) testbit((u)->gct, FINALIZEDBIT) 55 #define markfinalized(u) k_setbit((u)->gct, FINALIZEDBIT) 56 57 /* klisp: NOT USED YET */ 58 #define KEYWEAK bitmask(KEYWEAKBIT) 59 #define VALUEWEAK bitmask(VALUEWEAKBIT) 60 61 /* this one is klisp specific */ 62 #define markvaluearray(g, a, s) ({ \ 63 TValue *array_ = (a); \ 64 int32_t size_ = (s); \ 65 for(int32_t i_ = 0; i_ < size_; i_++, array_++) { \ 66 TValue mva_obj_ = *array_; \ 67 markvalue(g, mva_obj_); \ 68 }}) 69 70 #define markvalue(k,o) { checkconsistency(o); \ 71 if (iscollectable(o) && iswhite(gcvalue(o))) \ 72 reallymarkobject(k,gcvalue(o)); } 73 74 #define markobject(k,t) { if (iswhite(obj2gco(t))) \ 75 reallymarkobject(k, obj2gco(t)); } 76 77 78 #define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) 79 80 static void removeentry (Node *n) { 81 klisp_assert(ttisfree(gval(n))); 82 if (iscollectable(gkey(n)->this))/* dead key; remove it */ 83 gkey(n)->this = gc2deadkey(gcvalue(gkey(n)->this)); 84 } 85 86 static void reallymarkobject (global_State *g, GCObject *o) 87 { 88 klisp_assert(iswhite(o) && !isdead(g, o)); 89 white2gray(o); 90 /* klisp: most of klisp have the same structure, but conserve the switch 91 just in case. */ 92 uint8_t type = o->gch.tt; 93 switch (type) { 94 /* klisp: keep this around just in case we add it later */ 95 #if 0 96 case LUA_TUSERDATA: { 97 Table *mt = gco2u(o)->metatable; 98 gray2black(o); /* udata are never gray */ 99 if (mt) markobject(g, mt); 100 markobject(g, gco2u(o)->env); 101 return; 102 } 103 #endif 104 case K_TBIGRAT: /* the n & d are copied in the bigrat, not pointed to */ 105 case K_TBIGINT: 106 gray2black(o); /* bigint & bigrats are never gray */ 107 break; 108 case K_TPAIR: 109 case K_TSYMBOL: 110 case K_TKEYWORD: 111 case K_TSTRING: 112 case K_TENVIRONMENT: 113 case K_TCONTINUATION: 114 case K_TOPERATIVE: 115 case K_TAPPLICATIVE: 116 case K_TENCAPSULATION: 117 case K_TPROMISE: 118 case K_TTABLE: 119 case K_TERROR: 120 case K_TBYTEVECTOR: 121 case K_TVECTOR: 122 case K_TFPORT: 123 case K_TMPORT: 124 case K_TLIBRARY: 125 case K_TTHREAD: 126 case K_TMUTEX: 127 case K_TCONDVAR: 128 o->gch.gclist = g->gray; 129 g->gray = o; 130 break; 131 default: 132 /* shouldn't happen */ 133 fprintf(stderr, "Unknown GCObject type (in GC mark): %d\n", type); 134 abort(); 135 } 136 } 137 138 139 /* klisp: keep this around just in case we add it later */ 140 #if 0 141 static void marktmu (global_State *g) { 142 GCObject *u = g->tmudata; 143 if (u) { 144 do { 145 u = u->gch.next; 146 makewhite(g, u); /* may be marked, if left from previous GC */ 147 reallymarkobject(g, u); 148 } while (u != g->tmudata); 149 } 150 } 151 152 /* move `dead' udata that need finalization to list `tmudata' */ 153 size_t klispC_separateudata (lua_State *L, int all) { 154 global_State *g = G(L); 155 size_t deadmem = 0; 156 GCObject **p = &g->mainthread->next; 157 GCObject *curr; 158 while ((curr = *p) != NULL) { 159 if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) 160 p = &curr->gch.next; /* don't bother with them */ 161 else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { 162 markfinalized(gco2u(curr)); /* don't need finalization */ 163 p = &curr->gch.next; 164 } 165 else { /* must call its gc method */ 166 deadmem += sizeudata(gco2u(curr)); 167 markfinalized(gco2u(curr)); 168 *p = curr->gch.next; 169 /* link `curr' at the end of `tmudata' list */ 170 if (g->tmudata == NULL) /* list is empty? */ 171 /* creates a circular list */ 172 g->tmudata = curr->gch.next = curr; 173 else { 174 curr->gch.next = g->tmudata->gch.next; 175 g->tmudata->gch.next = curr; 176 g->tmudata = curr; 177 } 178 } 179 } 180 return deadmem; 181 } 182 183 #endif 184 185 static int32_t traversetable (global_State *g, Table *h) { 186 int32_t i; 187 TValue tv = gc2table(h); 188 int32_t weakkey = ktable_has_weak_keys(tv)? 1 : 0; 189 int32_t weakvalue = ktable_has_weak_values(tv)? 1 : 0; 190 191 if (weakkey || weakvalue) { /* is really weak? */ 192 h->gct &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ 193 h->gct |= cast(uint16_t, (weakkey << KEYWEAKBIT) | 194 (weakvalue << VALUEWEAKBIT)); 195 h->gclist = g->weak; /* must be cleared after GC, ... */ 196 g->weak = obj2gco(h); /* ... so put in the appropriate list */ 197 } 198 if (weakkey && weakvalue) return 1; 199 if (!weakvalue) { 200 i = h->sizearray; 201 while (i--) 202 markvalue(g, h->array[i]); 203 } 204 i = sizenode(h); 205 while (i--) { 206 Node *n = gnode(h, i); 207 klisp_assert(ttype(gkey(n)->this) != K_TDEADKEY || 208 ttisfree(gval(n))); 209 if (ttisfree(gval(n))) 210 removeentry(n); /* remove empty entries */ 211 else { 212 klisp_assert(!ttisfree(gkey(n)->this)); 213 if (!weakkey) markvalue(g, gkey(n)->this); 214 if (!weakvalue) markvalue(g, gval(n)); 215 } 216 } 217 return weakkey || weakvalue; 218 } 219 220 #if 0 221 /* 222 ** All marks are conditional because a GC may happen while the 223 ** prototype is still being created 224 */ 225 static void traverseproto (global_State *g, Proto *f) { 226 int i; 227 if (f->source) stringmark(f->source); 228 for (i=0; i<f->sizek; i++) /* mark literals */ 229 markvalue(g, &f->k[i]); 230 for (i=0; i<f->sizeupvalues; i++) { /* mark upvalue names */ 231 if (f->upvalues[i]) 232 stringmark(f->upvalues[i]); 233 } 234 for (i=0; i<f->sizep; i++) { /* mark nested protos */ 235 if (f->p[i]) 236 markobject(g, f->p[i]); 237 } 238 for (i=0; i<f->sizelocvars; i++) { /* mark local-variable names */ 239 if (f->locvars[i].varname) 240 stringmark(f->locvars[i].varname); 241 } 242 } 243 244 #endif 245 246 /* 247 ** traverse one gray object, turning it to black. 248 ** Returns `quantity' traversed. 249 */ 250 static int32_t propagatemark (global_State *g) { 251 GCObject *o = g->gray; 252 g->gray = o->gch.gclist; 253 klisp_assert(isgray(o)); 254 gray2black(o); 255 /* all types have si pointers */ 256 if (o->gch.si != NULL) { 257 markobject(g, o->gch.si); 258 } 259 uint8_t type = o->gch.tt; 260 261 switch (type) { 262 /* case K_TBIGRAT: 263 case K_TBIGINT: bigints & bigrats are never gray */ 264 case K_TPAIR: { 265 Pair *p = cast(Pair *, o); 266 markvalue(g, p->mark); 267 markvalue(g, p->car); 268 markvalue(g, p->cdr); 269 return sizeof(Pair); 270 } 271 case K_TSYMBOL: { 272 Symbol *s = cast(Symbol *, o); 273 markvalue(g, s->str); 274 return sizeof(Symbol); 275 } 276 case K_TKEYWORD: { 277 Keyword *k = cast(Keyword *, o); 278 markvalue(g, k->str); 279 return sizeof(Keyword); 280 } 281 case K_TSTRING: { 282 String *s = cast(String *, o); 283 markvalue(g, s->mark); 284 return sizeof(String) + (s->size + 1 * sizeof(char)); 285 } 286 case K_TENVIRONMENT: { 287 Environment *e = cast(Environment *, o); 288 markvalue(g, e->mark); 289 markvalue(g, e->parents); 290 markvalue(g, e->bindings); 291 markvalue(g, e->keyed_node); 292 markvalue(g, e->keyed_parents); 293 return sizeof(Environment); 294 } 295 case K_TCONTINUATION: { 296 Continuation *c = cast(Continuation *, o); 297 markvalue(g, c->mark); 298 markvalue(g, c->parent); 299 markvalue(g, c->comb); 300 markvaluearray(g, c->extra, c->extra_size); 301 return sizeof(Continuation) + sizeof(TValue) * c->extra_size; 302 } 303 case K_TOPERATIVE: { 304 Operative *op = cast(Operative *, o); 305 markvaluearray(g, op->extra, op->extra_size); 306 return sizeof(Operative) + sizeof(TValue) * op->extra_size; 307 } 308 case K_TAPPLICATIVE: { 309 Applicative *a = cast(Applicative *, o); 310 markvalue(g, a->underlying); 311 return sizeof(Applicative); 312 } 313 case K_TENCAPSULATION: { 314 Encapsulation *e = cast(Encapsulation *, o); 315 markvalue(g, e->key); 316 markvalue(g, e->value); 317 return sizeof(Encapsulation); 318 } 319 case K_TPROMISE: { 320 Promise *p = cast(Promise *, o); 321 markvalue(g, p->node); 322 return sizeof(Promise); 323 } 324 case K_TTABLE: { 325 Table *h = cast(Table *, o); 326 if (traversetable(g, h)) /* table is weak? */ 327 black2gray(o); /* keep it gray */ 328 return sizeof(Table) + sizeof(TValue) * h->sizearray + 329 sizeof(Node) * sizenode(h); 330 } 331 case K_TERROR: { 332 Error *e = cast(Error *, o); 333 markvalue(g, e->who); 334 markvalue(g, e->cont); 335 markvalue(g, e->msg); 336 markvalue(g, e->irritants); 337 return sizeof(Error); 338 } 339 case K_TBYTEVECTOR: { 340 Bytevector *b = cast(Bytevector *, o); 341 markvalue(g, b->mark); 342 return sizeof(Bytevector) + b->size * sizeof(uint8_t); 343 } 344 case K_TFPORT: { 345 FPort *p = cast(FPort *, o); 346 markvalue(g, p->filename); 347 return sizeof(FPort); 348 } 349 case K_TMPORT: { 350 MPort *p = cast(MPort *, o); 351 markvalue(g, p->filename); 352 markvalue(g, p->buf); 353 return sizeof(MPort); 354 } 355 case K_TVECTOR: { 356 Vector *v = cast(Vector *, o); 357 markvalue(g, v->mark); 358 markvaluearray(g, v->array, v->sizearray); 359 return sizeof(Vector) + v->sizearray * sizeof(TValue); 360 } 361 case K_TLIBRARY: { 362 Library *l = cast(Library *, o); 363 markvalue(g, l->env); 364 markvalue(g, l->exp_list); 365 return sizeof(Library); 366 } 367 case K_TTHREAD: { 368 klisp_State *K = cast(klisp_State *, o); 369 370 markvalue(g, K->curr_cont); 371 markvalue(g, K->next_obj); 372 markvalue(g, K->next_value); 373 markvalue(g, K->next_env); 374 markvalue(g, K->next_si); 375 /* NOTE: next_x_params is protected by next_obj */ 376 377 markvalue(g, K->shared_dict); 378 markvalue(g, K->curr_port); 379 380 /* Mark all objects in the auxiliary stack, 381 (all valid indexes are below top) and all the objects in 382 the two protected areas */ 383 markvaluearray(g, K->sbuf, K->stop); 384 markvaluearray(g, K->rooted_tvs_buf, K->rooted_tvs_top); 385 /* the area protecting variables is an array of type TValue *[] */ 386 TValue **ptr = K->rooted_vars_buf; 387 for (int i = 0, top = K->rooted_vars_top; i < top; i++, ptr++) { 388 markvalue(g, **ptr); 389 } 390 return sizeof(klisp_State) + (sizeof(TValue) * K->stop); 391 } 392 case K_TMUTEX: { 393 Mutex *m = cast(Mutex *, o); 394 395 markvalue(g, m->owner); 396 return sizeof(Mutex); 397 } 398 case K_TCONDVAR: { 399 Condvar *c = cast(Condvar *, o); 400 401 markvalue(g, c->mutex); 402 return sizeof(Condvar); 403 } 404 default: 405 fprintf(stderr, "Unknown GCObject type (in GC propagate): %d\n", 406 type); 407 abort(); 408 } 409 } 410 411 412 static size_t propagateall (global_State *g) { 413 size_t m = 0; 414 while (g->gray) m += propagatemark(g); 415 return m; 416 } 417 418 /* 419 ** The next function tells whether a key or value can be cleared from 420 ** a weak table. Non-collectable objects are never removed from weak 421 ** tables. Strings behave as `values', so are never removed too. for 422 ** other objects: if really collected, cannot keep them; for userdata 423 ** being finalized, keep them in keys, but not in values 424 */ 425 /* XXX what the hell is this, I should reread this part of the lua 426 source Andres Navarro */ 427 static int32_t iscleared (TValue o, int iskey) { 428 if (!iscollectable(o)) return 0; 429 #if 0 /* klisp: strings may be mutable... */ 430 if (ttisstring(o)) { 431 stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ 432 return 0; 433 } 434 #endif 435 return iswhite(gcvalue(o)); 436 437 /* klisp: keep around for later 438 || (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); 439 */ 440 } 441 442 443 /* 444 ** clear collected entries from weaktables 445 */ 446 static void cleartable (GCObject *l) { 447 while (l) { 448 Table *h = (Table *) (l); 449 int32_t i = h->sizearray; 450 klisp_assert(testbit(h->gct, VALUEWEAKBIT) || 451 testbit(h->gct, KEYWEAKBIT)); 452 if (testbit(h->gct, VALUEWEAKBIT)) { 453 while (i--) { 454 TValue *o = &h->array[i]; 455 if (iscleared(*o, 0)) /* value was collected? */ 456 *o = KFREE; /* remove value */ 457 } 458 } 459 i = sizenode(h); 460 while (i--) { 461 Node *n = gnode(h, i); 462 if (!ttisfree(gval(n)) && /* non-empty entry? */ 463 (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { 464 gval(n) = KFREE; /* remove value ... */ 465 removeentry(n); /* remove entry from table */ 466 } 467 } 468 l = h->gclist; 469 } 470 } 471 472 static void freeobj (klisp_State *K, GCObject *o) { 473 /* TODO use specific functions like in bigint, bigrat & table */ 474 uint8_t type = o->gch.tt; 475 switch (type) { 476 case K_TBIGINT: { 477 mp_int_free(K, (Bigint *)o); 478 break; 479 } 480 case K_TBIGRAT: { 481 mp_rat_free(K, (Bigrat *)o); 482 break; 483 } 484 case K_TPAIR: 485 klispM_free(K, (Pair *)o); 486 break; 487 case K_TSYMBOL: 488 /* symbols are in the string/symbol table */ 489 /* The string will be freed before/after */ 490 /* symbols with no source info are in the string/symbol table */ 491 if (ttisnil(ktry_get_si(K, gc2sym(o)))) 492 G(K)->strt.nuse--; 493 klispM_free(K, (Symbol *)o); 494 break; 495 case K_TKEYWORD: 496 /* keywords are in the string table */ 497 /* The string will be freed before/after */ 498 G(K)->strt.nuse--; 499 klispM_free(K, (Keyword *)o); 500 break; 501 case K_TSTRING: 502 /* immutable strings are in the string/symbol table */ 503 if (kstring_immutablep(gc2str(o))) 504 G(K)->strt.nuse--; 505 klispM_freemem(K, o, sizeof(String)+o->str.size+1); 506 break; 507 case K_TENVIRONMENT: 508 klispM_free(K, (Environment *)o); 509 break; 510 case K_TCONTINUATION: 511 klispM_freemem(K, o, sizeof(Continuation) + 512 o->cont.extra_size * sizeof(TValue)); 513 break; 514 case K_TOPERATIVE: 515 klispM_freemem(K, o, sizeof(Operative) + 516 o->op.extra_size * sizeof(TValue)); 517 break; 518 case K_TAPPLICATIVE: 519 klispM_free(K, (Applicative *)o); 520 break; 521 case K_TENCAPSULATION: 522 klispM_free(K, (Encapsulation *)o); 523 break; 524 case K_TPROMISE: 525 klispM_free(K, (Promise *)o); 526 break; 527 case K_TTABLE: 528 klispH_free(K, (Table *)o); 529 break; 530 case K_TERROR: 531 klispE_free(K, (Error *)o); 532 break; 533 case K_TBYTEVECTOR: 534 /* immutable bytevectors are in the string/symbol table */ 535 if (kbytevector_immutablep(gc2str(o))) 536 G(K)->strt.nuse--; 537 klispM_freemem(K, o, sizeof(Bytevector)+o->bytevector.size); 538 break; 539 case K_TFPORT: 540 /* first close the port to free the FILE structure. 541 This works even if the port was already closed, 542 it is important that this don't throw errors, because 543 the mechanism used in error handling would crash at this 544 point */ 545 kclose_port(K, gc2fport(o)); 546 klispM_free(K, (FPort *)o); 547 break; 548 case K_TMPORT: 549 /* memory ports (string & bytevector) don't need to be closed 550 explicitly */ 551 klispM_free(K, (MPort *)o); 552 break; 553 case K_TVECTOR: 554 klispM_freemem(K, o, sizeof(Vector) + sizeof(TValue) * o->vector.sizearray); 555 break; 556 case K_TLIBRARY: 557 klispM_free(K, (Library *)o); 558 break; 559 case K_TTHREAD: { 560 klisp_State *K2 = (klisp_State *) o; 561 562 klisp_assert(K2 != G(K)->mainthread); 563 klisp_assert(K2 != K); 564 /* threads are always created detached, so there's no 565 need to do a join here */ 566 klispT_freethread(K, K2); 567 break; 568 } 569 case K_TMUTEX: 570 klispX_free(K, (Mutex *) o); 571 break; 572 case K_TCONDVAR: 573 klispV_free(K, (Condvar *) o); 574 break; 575 default: 576 /* shouldn't happen */ 577 fprintf(stderr, "Unknown GCObject type (in GC free): %d\n", 578 type); 579 abort(); 580 } 581 } 582 583 584 /* klisp can't have more than 4gb */ 585 #define sweepwholelist(K,p) sweeplist(K,p,UINT32_MAX) 586 587 588 static GCObject **sweeplist (klisp_State *K, GCObject **p, uint32_t count) 589 { 590 GCObject *curr; 591 global_State *g = G(K); 592 int deadmask = otherwhite(g); 593 while ((curr = *p) != NULL && count-- > 0) { 594 if ((curr->gch.gct ^ WHITEBITS) & deadmask) { /* not dead? */ 595 klisp_assert(!isdead(g, curr) || testbit(curr->gch.gct, FIXEDBIT)); 596 makewhite(g, curr); /* make it white (for next cycle) */ 597 p = &curr->gch.next; 598 } else { /* must erase `curr' */ 599 klisp_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); 600 *p = curr->gch.next; 601 if (curr == g->rootgc) /* is the first element of the list? */ 602 g->rootgc = curr->gch.next; /* adjust first */ 603 freeobj(K, curr); 604 } 605 } 606 return p; 607 } 608 609 static void checkSizes (klisp_State *K) { 610 global_State *g = G(K); 611 /* check size of string/symbol hash */ 612 if (g->strt.nuse < cast(uint32_t , g->strt.size/4) && 613 g->strt.size > MINSTRTABSIZE*2) 614 klispS_resize(K, g->strt.size/2); /* table is too big */ 615 #if 0 /* not used in klisp */ 616 /* check size of buffer */ 617 if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ 618 size_t newsize = luaZ_sizebuffer(&g->buff) / 2; 619 luaZ_resizebuffer(L, &g->buff, newsize); 620 } 621 #endif 622 } 623 624 #if 0 /* klisp: keep this around */ 625 static void GCTM (lua_State *L) { 626 global_State *g = G(L); 627 GCObject *o = g->tmudata->gch.next; /* get first element */ 628 Udata *udata = rawgco2u(o); 629 const TValue *tm; 630 /* remove udata from `tmudata' */ 631 if (o == g->tmudata) /* last element? */ 632 g->tmudata = NULL; 633 else 634 g->tmudata->gch.next = udata->uv.next; 635 udata->uv.next = g->mainthread->next; /* return it to `root' list */ 636 g->mainthread->next = o; 637 makewhite(g, o); 638 tm = fasttm(L, udata->uv.metatable, TM_GC); 639 if (tm != NULL) { 640 lu_byte oldah = L->allowhook; 641 lu_mem oldt = g->GCthreshold; 642 L->allowhook = 0; /* stop debug hooks during GC tag method */ 643 g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ 644 setobj2s(L, L->top, tm); 645 setuvalue(L, L->top+1, udata); 646 L->top += 2; 647 luaD_call(L, L->top - 2, 0); 648 L->allowhook = oldah; /* restore hooks */ 649 g->GCthreshold = oldt; /* restore threshold */ 650 } 651 } 652 653 654 /* 655 ** Call all GC tag methods 656 */ 657 void klispC_callGCTM (lua_State *L) { 658 while (G(L)->tmudata) 659 GCTM(L); 660 } 661 #endif 662 663 /* This still leaves allocated objs in K, namely the 664 arrays that aren't TValues */ 665 void klispC_freeall (klisp_State *K) { 666 global_State *g = G(K); 667 /* mask to collect all elements */ 668 g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); 669 sweepwholelist(K, &g->rootgc); 670 /* free all keyword/symbol/string/bytevectors lists */ 671 for (int32_t i = 0; i < g->strt.size; i++) 672 sweepwholelist(K, &g->strt.hash[i]); 673 } 674 675 /* mark root set */ 676 static void markroot (klisp_State *K) { 677 global_State *g = G(K); 678 g->gray = NULL; 679 g->grayagain = NULL; 680 g->weak = NULL; 681 682 markobject(g, g->mainthread); /* this is also in the thread table */ 683 684 markvalue(g, g->name_table); 685 markvalue(g, g->cont_name_table); 686 markvalue(g, g->thread_table); 687 688 markvalue(g, g->eval_op); 689 markvalue(g, g->list_app); 690 markvalue(g, g->memoize_app); 691 markvalue(g, g->ground_env); 692 markvalue(g, g->module_params_sym); 693 markvalue(g, g->root_cont); 694 markvalue(g, g->error_cont); 695 markvalue(g, g->system_error_cont); 696 697 markvalue(g, g->kd_in_port_key); 698 markvalue(g, g->kd_out_port_key); 699 markvalue(g, g->kd_error_port_key); 700 markvalue(g, g->kd_strict_arith_key); 701 markvalue(g, g->empty_string); 702 markvalue(g, g->empty_bytevector); 703 markvalue(g, g->empty_vector); 704 705 markvalue(g, g->ktok_lparen); 706 markvalue(g, g->ktok_rparen); 707 markvalue(g, g->ktok_dot); 708 markvalue(g, g->ktok_sexp_comment); 709 710 markvalue(g, g->require_path); 711 markvalue(g, g->require_table); 712 713 markvalue(g, g->libraries_registry); 714 715 g->gcstate = GCSpropagate; 716 } 717 718 static void atomic (klisp_State *K) { 719 global_State *g = G(K); 720 size_t udsize; /* total size of userdata to be finalized */ 721 /* traverse objects caught by write barrier */ 722 propagateall(g); 723 724 /* remark weak tables */ 725 g->gray = g->weak; 726 g->weak = NULL; 727 propagateall(g); 728 729 /* remark gray again */ 730 g->gray = g->grayagain; 731 g->grayagain = NULL; 732 propagateall(g); 733 734 udsize = 0; /* to init var 'till we add user data */ 735 #if 0 /* keep around */ 736 udsize = klispC_separateudata(L, 0); /* separate userdata to be finalized */ 737 marktmu(g); /* mark `preserved' userdata */ 738 udsize += propagateall(g); /* remark, to propagate `preserveness' */ 739 #endif 740 cleartable(g->weak); /* remove collected objects from weak tables */ 741 742 /* flip current white */ 743 g->currentwhite = cast(uint16_t, otherwhite(g)); 744 g->sweepstrgc = 0; 745 g->sweepgc = &g->rootgc; 746 g->gcstate = GCSsweepstring; 747 g->estimate = g->totalbytes - udsize; /* first estimate */ 748 } 749 750 751 static int32_t singlestep (klisp_State *K) { 752 global_State *g = G(K); 753 switch (g->gcstate) { 754 case GCSpause: { 755 markroot(K); /* start a new collection */ 756 return 0; 757 } 758 case GCSpropagate: { 759 if (g->gray) 760 return propagatemark(g); 761 else { /* no more `gray' objects */ 762 atomic(K); /* finish mark phase */ 763 return 0; 764 } 765 } 766 case GCSsweepstring: { 767 uint32_t old = g->totalbytes; 768 sweepwholelist(K, &g->strt.hash[g->sweepstrgc++]); 769 if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ 770 g->gcstate = GCSsweep; /* end sweep-string phase */ 771 klisp_assert(old >= g->totalbytes); 772 g->estimate -= old - g->totalbytes; 773 return GCSWEEPCOST; 774 } 775 case GCSsweep: { 776 uint32_t old = g->totalbytes; 777 g->sweepgc = sweeplist(K, g->sweepgc, GCSWEEPMAX); 778 if (*g->sweepgc == NULL) { /* nothing more to sweep? */ 779 checkSizes(K); 780 g->gcstate = GCSfinalize; /* end sweep phase */ 781 } 782 klisp_assert(old >= g->totalbytes); 783 g->estimate -= old - g->totalbytes; 784 return GCSWEEPMAX*GCSWEEPCOST; 785 } 786 case GCSfinalize: { 787 #if 0 /* keep around */ 788 if (g->tmudata) { 789 GCTM(L); 790 if (g->estimate > GCFINALIZECOST) 791 g->estimate -= GCFINALIZECOST; 792 return GCFINALIZECOST; 793 } 794 else { 795 #endif 796 g->gcstate = GCSpause; /* end collection */ 797 g->gcdept = 0; 798 return 0; 799 #if 0 800 } 801 #endif 802 } 803 default: klisp_assert(0); return 0; 804 } 805 } 806 807 808 void klispC_step (klisp_State *K) { 809 global_State *g = G(K); 810 int32_t lim = (GCSTEPSIZE/100) * g->gcstepmul; 811 812 if (lim == 0) 813 lim = (UINT32_MAX-1)/2; /* no limit */ 814 815 g->gcdept += g->totalbytes - g->GCthreshold; 816 817 do { 818 lim -= singlestep(K); 819 if (g->gcstate == GCSpause) 820 break; 821 } while (lim > 0); 822 823 if (g->gcstate != GCSpause) { 824 if (g->gcdept < GCSTEPSIZE) { 825 g->GCthreshold = g->totalbytes + GCSTEPSIZE; 826 /* - lim/g->gcstepmul;*/ 827 } else { 828 g->gcdept -= GCSTEPSIZE; 829 g->GCthreshold = g->totalbytes; 830 } 831 } else { 832 klisp_assert(g->totalbytes >= g->estimate); 833 setthreshold(g); 834 } 835 } 836 837 void klispC_fullgc (klisp_State *K) { 838 global_State *g = G(K); 839 if (g->gcstate <= GCSpropagate) { 840 /* reset sweep marks to sweep all elements (returning them to white) */ 841 g->sweepstrgc = 0; 842 g->sweepgc = &g->rootgc; 843 /* reset other collector lists */ 844 g->gray = NULL; 845 g->grayagain = NULL; 846 g->weak = NULL; 847 g->gcstate = GCSsweepstring; 848 } 849 klisp_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); 850 /* finish any pending sweep phase */ 851 while (g->gcstate != GCSfinalize) { 852 klisp_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); 853 singlestep(K); 854 } 855 markroot(K); 856 while (g->gcstate != GCSpause) { 857 singlestep(K); 858 } 859 setthreshold(g); 860 } 861 862 /* TODO: make all code using mutation to call these, 863 this is actually the only thing that is missing for an incremental 864 garbage collector! 865 IMPORTANT: a call to maybe a different but similar function should be 866 made before assigning to a GC guarded variable, or pushed in a GC 867 guarded stack! */ 868 void klispC_barrierf (klisp_State *K, GCObject *o, GCObject *v) { 869 global_State *g = G(K); 870 klisp_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); 871 klisp_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); 872 klisp_assert(o->gch.tt != K_TTABLE); 873 /* must keep invariant? */ 874 if (g->gcstate == GCSpropagate) 875 reallymarkobject(g, v); /* restore invariant */ 876 else /* don't mind */ 877 makewhite(g, o); /* mark as white just to avoid other barriers */ 878 } 879 880 void klispC_barrierback (klisp_State *K, Table *t) { 881 global_State *g = G(K); 882 GCObject *o = obj2gco(t); 883 klisp_assert(isblack(o) && !isdead(g, o)); 884 klisp_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); 885 black2gray(o); /* make table gray (again) */ 886 t->gclist = g->grayagain; 887 g->grayagain = o; 888 } 889 890 /* NOTE: kflags is added for klisp */ 891 /* NOTE: symbols, keywords, immutable strings and immutable bytevectors do 892 this "by hand", they don't call this */ 893 void klispC_link (klisp_State *K, GCObject *o, uint8_t tt, uint8_t kflags) { 894 global_State *g = G(K); 895 o->gch.next = g->rootgc; 896 g->rootgc = o; 897 o->gch.gct = klispC_white(g); 898 o->gch.tt = tt; 899 o->gch.kflags = kflags; 900 o->gch.si = NULL; 901 /* NOTE that o->gch.gclist doesn't need to be setted */ 902 } 903