diff options
Diffstat (limited to 'src/lj_gc.c')
-rw-r--r-- | src/lj_gc.c | 103 |
1 files changed, 75 insertions, 28 deletions
diff --git a/src/lj_gc.c b/src/lj_gc.c index 793d8dac..f231b1ce 100644 --- a/src/lj_gc.c +++ b/src/lj_gc.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include "lj_state.h" | 20 | #include "lj_state.h" |
21 | #include "lj_frame.h" | 21 | #include "lj_frame.h" |
22 | #if LJ_HASFFI | 22 | #if LJ_HASFFI |
23 | #include "lj_ctype.h" | ||
23 | #include "lj_cdata.h" | 24 | #include "lj_cdata.h" |
24 | #endif | 25 | #endif |
25 | #include "lj_trace.h" | 26 | #include "lj_trace.h" |
@@ -170,8 +171,9 @@ static int gc_traverse_tab(global_State *g, GCtab *t) | |||
170 | while ((c = *modestr++)) { | 171 | while ((c = *modestr++)) { |
171 | if (c == 'k') weak |= LJ_GC_WEAKKEY; | 172 | if (c == 'k') weak |= LJ_GC_WEAKKEY; |
172 | else if (c == 'v') weak |= LJ_GC_WEAKVAL; | 173 | else if (c == 'v') weak |= LJ_GC_WEAKVAL; |
174 | else if (c == 'K') weak = (int)(~0u & ~LJ_GC_WEAKVAL); | ||
173 | } | 175 | } |
174 | if (weak) { /* Weak tables are cleared in the atomic phase. */ | 176 | if (weak > 0) { /* Weak tables are cleared in the atomic phase. */ |
175 | t->marked = cast_byte((t->marked & ~LJ_GC_WEAK) | weak); | 177 | t->marked = cast_byte((t->marked & ~LJ_GC_WEAK) | weak); |
176 | setgcrefr(t->gclist, g->gc.weak); | 178 | setgcrefr(t->gclist, g->gc.weak); |
177 | setgcref(g->gc.weak, obj2gco(t)); | 179 | setgcref(g->gc.weak, obj2gco(t)); |
@@ -458,53 +460,98 @@ static void gc_clearweak(GCobj *o) | |||
458 | } | 460 | } |
459 | } | 461 | } |
460 | 462 | ||
461 | /* Finalize one userdata object from mmudata list. */ | 463 | /* Call a userdata or cdata finalizer. */ |
464 | static void gc_call_finalizer(global_State *g, lua_State *L, | ||
465 | cTValue *mo, GCobj *o) | ||
466 | { | ||
467 | /* Save and restore lots of state around the __gc callback. */ | ||
468 | uint8_t oldh = hook_save(g); | ||
469 | MSize oldt = g->gc.threshold; | ||
470 | int errcode; | ||
471 | TValue *top; | ||
472 | lj_trace_abort(g); | ||
473 | top = L->top; | ||
474 | L->top = top+2; | ||
475 | hook_entergc(g); /* Disable hooks and new traces during __gc. */ | ||
476 | g->gc.threshold = LJ_MAX_MEM; /* Prevent GC steps. */ | ||
477 | copyTV(L, top, mo); | ||
478 | setgcV(L, top+1, o, ~o->gch.gct); | ||
479 | errcode = lj_vm_pcall(L, top+1, 1+0, -1); /* Stack: |mo|o| -> | */ | ||
480 | hook_restore(g, oldh); | ||
481 | g->gc.threshold = oldt; /* Restore GC threshold. */ | ||
482 | if (errcode) | ||
483 | lj_err_throw(L, errcode); /* Propagate errors. */ | ||
484 | } | ||
485 | |||
486 | /* Finalize one userdata or cdata object from the mmudata list. */ | ||
462 | static void gc_finalize(lua_State *L) | 487 | static void gc_finalize(lua_State *L) |
463 | { | 488 | { |
464 | global_State *g = G(L); | 489 | global_State *g = G(L); |
465 | GCobj *o = gcnext(gcref(g->gc.mmudata)); | 490 | GCobj *o = gcnext(gcref(g->gc.mmudata)); |
466 | GCudata *ud = gco2ud(o); | ||
467 | cTValue *mo; | 491 | cTValue *mo; |
468 | lua_assert(gcref(g->jit_L) == NULL); /* Must not be called on trace. */ | 492 | lua_assert(gcref(g->jit_L) == NULL); /* Must not be called on trace. */ |
469 | /* Unchain from list of userdata to be finalized. */ | 493 | /* Unchain from list of userdata to be finalized. */ |
470 | if (o == gcref(g->gc.mmudata)) | 494 | if (o == gcref(g->gc.mmudata)) |
471 | setgcrefnull(g->gc.mmudata); | 495 | setgcrefnull(g->gc.mmudata); |
472 | else | 496 | else |
473 | setgcrefr(gcref(g->gc.mmudata)->gch.nextgc, ud->nextgc); | 497 | setgcrefr(gcref(g->gc.mmudata)->gch.nextgc, o->gch.nextgc); |
474 | /* Add it back to the main userdata list and make it white. */ | ||
475 | setgcrefr(ud->nextgc, mainthread(g)->nextgc); | ||
476 | setgcref(mainthread(g)->nextgc, o); | ||
477 | makewhite(g, o); | 498 | makewhite(g, o); |
478 | /* Resolve the __gc metamethod. */ | 499 | #if LJ_HASFFI |
479 | mo = lj_meta_fastg(g, tabref(ud->metatable), MM_gc); | 500 | if (o->gch.gct == ~LJ_TCDATA) { |
480 | if (mo) { | 501 | TValue tmp, *tv; |
481 | /* Save and restore lots of state around the __gc callback. */ | 502 | setgcrefr(o->gch.nextgc, g->gc.root); /* Add cdata back to the gc list. */ |
482 | uint8_t oldh = hook_save(g); | 503 | setgcref(g->gc.root, o); |
483 | MSize oldt = g->gc.threshold; | 504 | /* Resolve finalizer. */ |
484 | int errcode; | 505 | setcdataV(L, &tmp, gco2cd(o)); |
485 | TValue *top; | 506 | tv = lj_tab_set(L, ctype_ctsG(g)->finalizer, &tmp); |
486 | lj_trace_abort(g); | 507 | if (!tvisnil(tv)) { |
487 | top = L->top; | 508 | copyTV(L, &tmp, tv); |
488 | L->top = top+2; | 509 | setnilV(tv); /* Clear entry in finalizer table. */ |
489 | hook_entergc(g); /* Disable hooks and new traces during __gc. */ | 510 | gc_call_finalizer(g, L, &tmp, o); |
490 | g->gc.threshold = LJ_MAX_MEM; /* Prevent GC steps. */ | 511 | } |
491 | copyTV(L, top, mo); | 512 | return; |
492 | setudataV(L, top+1, ud); | ||
493 | errcode = lj_vm_pcall(L, top+1, 1+0, -1); /* Stack: |mo|ud| -> | */ | ||
494 | hook_restore(g, oldh); | ||
495 | g->gc.threshold = oldt; /* Restore GC threshold. */ | ||
496 | if (errcode) | ||
497 | lj_err_throw(L, errcode); /* Propagate errors. */ | ||
498 | } | 513 | } |
514 | #endif | ||
515 | /* Add userdata back to the main userdata list. */ | ||
516 | setgcrefr(o->gch.nextgc, mainthread(g)->nextgc); | ||
517 | setgcref(mainthread(g)->nextgc, o); | ||
518 | /* Resolve the __gc metamethod. */ | ||
519 | mo = lj_meta_fastg(g, tabref(gco2ud(o)->metatable), MM_gc); | ||
520 | if (mo) | ||
521 | gc_call_finalizer(g, L, mo, o); | ||
499 | } | 522 | } |
500 | 523 | ||
501 | /* Finalize all userdata objects from mmudata list. */ | 524 | /* Finalize all userdata objects from mmudata list. */ |
502 | void lj_gc_finalizeudata(lua_State *L) | 525 | void lj_gc_finalize_udata(lua_State *L) |
503 | { | 526 | { |
504 | while (gcref(G(L)->gc.mmudata) != NULL) | 527 | while (gcref(G(L)->gc.mmudata) != NULL) |
505 | gc_finalize(L); | 528 | gc_finalize(L); |
506 | } | 529 | } |
507 | 530 | ||
531 | #if LJ_HASFFI | ||
532 | /* Finalize all cdata objects from finalizer table. */ | ||
533 | void lj_gc_finalize_cdata(lua_State *L) | ||
534 | { | ||
535 | global_State *g = G(L); | ||
536 | CTState *cts = ctype_ctsG(g); | ||
537 | if (cts) { | ||
538 | GCtab *t = cts->finalizer; | ||
539 | Node *node = noderef(t->node); | ||
540 | ptrdiff_t i; | ||
541 | setgcrefnull(t->metatable); /* Mark finalizer table as disabled. */ | ||
542 | for (i = (ptrdiff_t)t->hmask; i >= 0; i--) | ||
543 | if (!tvisnil(&node[i].val) && tviscdata(&node[i].key)) { | ||
544 | GCobj *o = gcV(&node[i].key); | ||
545 | TValue tmp; | ||
546 | o->gch.marked &= ~LJ_GC_CDATA_FIN; | ||
547 | copyTV(L, &tmp, &node[i].val); | ||
548 | setnilV(&node[i].val); | ||
549 | gc_call_finalizer(g, L, &tmp, o); | ||
550 | } | ||
551 | } | ||
552 | } | ||
553 | #endif | ||
554 | |||
508 | /* Free all remaining GC objects. */ | 555 | /* Free all remaining GC objects. */ |
509 | void lj_gc_freeall(global_State *g) | 556 | void lj_gc_freeall(global_State *g) |
510 | { | 557 | { |