diff options
| author | Mike Pall <mike> | 2011-02-28 16:48:13 +0100 |
|---|---|---|
| committer | Mike Pall <mike> | 2011-02-28 16:48:13 +0100 |
| commit | 83a37aeca74724ef76dee7c8246bdbb88132940d (patch) | |
| tree | 9dca0cd0aa13cf911ad26c3655533a72bb6790a9 | |
| parent | cead25f928ac606fc1a13882b818913aab3635a9 (diff) | |
| download | luajit-83a37aeca74724ef76dee7c8246bdbb88132940d.tar.gz luajit-83a37aeca74724ef76dee7c8246bdbb88132940d.tar.bz2 luajit-83a37aeca74724ef76dee7c8246bdbb88132940d.zip | |
FFI: Add ffi.gc() function for finalization of cdata objects.
| -rw-r--r-- | doc/ext_ffi_api.html | 28 | ||||
| -rw-r--r-- | src/Makefile.dep | 10 | ||||
| -rw-r--r-- | src/lib_ffi.c | 77 | ||||
| -rw-r--r-- | src/lj_cdata.c | 13 | ||||
| -rw-r--r-- | src/lj_ctype.c | 3 | ||||
| -rw-r--r-- | src/lj_ctype.h | 3 | ||||
| -rw-r--r-- | src/lj_gc.c | 103 | ||||
| -rw-r--r-- | src/lj_gc.h | 8 | ||||
| -rw-r--r-- | src/lj_state.c | 3 |
9 files changed, 191 insertions, 57 deletions
diff --git a/doc/ext_ffi_api.html b/doc/ext_ffi_api.html index b8c52fb6..5bd4b80c 100644 --- a/doc/ext_ffi_api.html +++ b/doc/ext_ffi_api.html | |||
| @@ -238,6 +238,34 @@ This functions is mainly useful to override the pointer compatibility | |||
| 238 | checks or to convert pointers to addresses or vice versa. | 238 | checks or to convert pointers to addresses or vice versa. |
| 239 | </p> | 239 | </p> |
| 240 | 240 | ||
| 241 | <h3 id="ffi_gc"><tt>cdata = ffi.gc(cdata, finalizer)</tt></h3> | ||
| 242 | <p> | ||
| 243 | Associates a finalizer with a pointer or aggregate cdata object. The | ||
| 244 | cdata object is returned unchanged. | ||
| 245 | </p> | ||
| 246 | <p> | ||
| 247 | This function allows safe integration of unmanaged resources into the | ||
| 248 | automatic memory management of the LuaJIT garbage collector. Typical | ||
| 249 | usage: | ||
| 250 | </p> | ||
| 251 | <pre class="code"> | ||
| 252 | local p = ffi.gc(ffi.C.malloc(n), ffi.C.free) | ||
| 253 | ... | ||
| 254 | p = nil -- Last reference to p is gone. | ||
| 255 | -- GC will eventually run finalizer: ffi.C.free(p) | ||
| 256 | </pre> | ||
| 257 | <p> | ||
| 258 | A cdata finalizer works like the <tt>__gc</tt> metamethod for userdata | ||
| 259 | objects: when the last reference to a cdata object is gone, the | ||
| 260 | associated finalizer is called with the cdata object as an argument. The | ||
| 261 | finalizer can be a Lua function or a cdata function or cdata function | ||
| 262 | pointer. An existing finalizer can be removed by setting a <tt>nil</tt> | ||
| 263 | finalizer, e.g. right before explicitly deleting a resource: | ||
| 264 | </p> | ||
| 265 | <pre class="code"> | ||
| 266 | ffi.C.free(ffi.gc(p, nil)) -- Manually free the memory. | ||
| 267 | </pre> | ||
| 268 | |||
| 241 | <h2 id="info">C Type Information</h2> | 269 | <h2 id="info">C Type Information</h2> |
| 242 | <p> | 270 | <p> |
| 243 | The following API functions return information about C types. | 271 | The following API functions return information about C types. |
diff --git a/src/Makefile.dep b/src/Makefile.dep index 7f87155e..1684ebd7 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep | |||
| @@ -21,9 +21,9 @@ lib_bit.o: lib_bit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ | |||
| 21 | lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ | 21 | lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ |
| 22 | lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h lj_libdef.h | 22 | lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h lj_libdef.h |
| 23 | lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ | 23 | lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ |
| 24 | lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ctype.h lj_cparse.h \ | 24 | lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h \ |
| 25 | lj_cdata.h lj_cconv.h lj_carith.h lj_ccall.h lj_clib.h lj_ff.h \ | 25 | lj_cparse.h lj_cdata.h lj_cconv.h lj_carith.h lj_ccall.h lj_clib.h \ |
| 26 | lj_ffdef.h lj_lib.h lj_libdef.h | 26 | lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h |
| 27 | lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h | 27 | lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h |
| 28 | lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ | 28 | lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ |
| 29 | lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ff.h lj_ffdef.h \ | 29 | lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ff.h lj_ffdef.h \ |
| @@ -96,7 +96,7 @@ lj_func.o: lj_func.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ | |||
| 96 | lj_traceerr.h lj_vm.h | 96 | lj_traceerr.h lj_vm.h |
| 97 | lj_gc.o: lj_gc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ | 97 | lj_gc.o: lj_gc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
| 98 | lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_udata.h lj_meta.h \ | 98 | lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_udata.h lj_meta.h \ |
| 99 | lj_state.h lj_frame.h lj_bc.h lj_cdata.h lj_ctype.h lj_trace.h lj_jit.h \ | 99 | lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_cdata.h lj_trace.h lj_jit.h \ |
| 100 | lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h | 100 | lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h |
| 101 | lj_gdbjit.o: lj_gdbjit.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | 101 | lj_gdbjit.o: lj_gdbjit.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
| 102 | lj_gc.h lj_err.h lj_errmsg.h lj_frame.h lj_bc.h lj_jit.h lj_ir.h \ | 102 | lj_gc.h lj_err.h lj_errmsg.h lj_frame.h lj_bc.h lj_jit.h lj_ir.h \ |
| @@ -161,7 +161,7 @@ lj_vmevent.o: lj_vmevent.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | |||
| 161 | lj_vm.h lj_vmevent.h | 161 | lj_vm.h lj_vmevent.h |
| 162 | ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \ | 162 | ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \ |
| 163 | lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h \ | 163 | lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h \ |
| 164 | lj_udata.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_cdata.h lj_ctype.h \ | 164 | lj_udata.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_cdata.h \ |
| 165 | lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_err.c \ | 165 | lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_err.c \ |
| 166 | lj_ff.h lj_ffdef.h lj_char.c lj_char.h lj_bc.c lj_bcdef.h lj_obj.c \ | 166 | lj_ff.h lj_ffdef.h lj_char.c lj_char.h lj_bc.c lj_bcdef.h lj_obj.c \ |
| 167 | lj_str.c lj_tab.c lj_func.c lj_udata.c lj_meta.c lj_state.c lj_lex.h \ | 167 | lj_str.c lj_tab.c lj_func.c lj_udata.c lj_meta.c lj_state.c lj_lex.h \ |
diff --git a/src/lib_ffi.c b/src/lib_ffi.c index e88b6f54..674bbf00 100644 --- a/src/lib_ffi.c +++ b/src/lib_ffi.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "lj_gc.h" | 17 | #include "lj_gc.h" |
| 18 | #include "lj_err.h" | 18 | #include "lj_err.h" |
| 19 | #include "lj_str.h" | 19 | #include "lj_str.h" |
| 20 | #include "lj_tab.h" | ||
| 20 | #include "lj_ctype.h" | 21 | #include "lj_ctype.h" |
| 21 | #include "lj_cparse.h" | 22 | #include "lj_cparse.h" |
| 22 | #include "lj_cdata.h" | 23 | #include "lj_cdata.h" |
| @@ -353,6 +354,24 @@ LJLIB_CF(ffi_new) LJLIB_REC(.) | |||
| 353 | return 1; | 354 | return 1; |
| 354 | } | 355 | } |
| 355 | 356 | ||
| 357 | LJLIB_CF(ffi_cast) LJLIB_REC(ffi_new) | ||
| 358 | { | ||
| 359 | CTState *cts = ctype_cts(L); | ||
| 360 | CTypeID id = ffi_checkctype(L, cts); | ||
| 361 | CType *d = ctype_raw(cts, id); | ||
| 362 | TValue *o = lj_lib_checkany(L, 2); | ||
| 363 | L->top = o+1; /* Make sure this is the last item on the stack. */ | ||
| 364 | if (!(ctype_isnum(d->info) || ctype_isptr(d->info) || ctype_isenum(d->info))) | ||
| 365 | lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE); | ||
| 366 | if (!(tviscdata(o) && cdataV(o)->typeid == id)) { | ||
| 367 | GCcdata *cd = lj_cdata_new(cts, id, d->size); | ||
| 368 | lj_cconv_ct_tv(cts, d, cdataptr(cd), o, CCF_CAST); | ||
| 369 | setcdataV(L, o, cd); | ||
| 370 | lj_gc_check(L); | ||
| 371 | } | ||
| 372 | return 1; | ||
| 373 | } | ||
| 374 | |||
| 356 | LJLIB_CF(ffi_typeof) | 375 | LJLIB_CF(ffi_typeof) |
| 357 | { | 376 | { |
| 358 | CTState *cts = ctype_cts(L); | 377 | CTState *cts = ctype_cts(L); |
| @@ -419,24 +438,6 @@ LJLIB_CF(ffi_offsetof) | |||
| 419 | return 0; | 438 | return 0; |
| 420 | } | 439 | } |
| 421 | 440 | ||
| 422 | LJLIB_CF(ffi_cast) LJLIB_REC(ffi_new) | ||
| 423 | { | ||
| 424 | CTState *cts = ctype_cts(L); | ||
| 425 | CTypeID id = ffi_checkctype(L, cts); | ||
| 426 | CType *d = ctype_raw(cts, id); | ||
| 427 | TValue *o = lj_lib_checkany(L, 2); | ||
| 428 | L->top = o+1; /* Make sure this is the last item on the stack. */ | ||
| 429 | if (!(ctype_isnum(d->info) || ctype_isptr(d->info) || ctype_isenum(d->info))) | ||
| 430 | lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE); | ||
| 431 | if (!(tviscdata(o) && cdataV(o)->typeid == id)) { | ||
| 432 | GCcdata *cd = lj_cdata_new(cts, id, d->size); | ||
| 433 | lj_cconv_ct_tv(cts, d, cdataptr(cd), o, CCF_CAST); | ||
| 434 | setcdataV(L, o, cd); | ||
| 435 | lj_gc_check(L); | ||
| 436 | } | ||
| 437 | return 1; | ||
| 438 | } | ||
| 439 | |||
| 440 | LJLIB_CF(ffi_string) LJLIB_REC(.) | 441 | LJLIB_CF(ffi_string) LJLIB_REC(.) |
| 441 | { | 442 | { |
| 442 | CTState *cts = ctype_cts(L); | 443 | CTState *cts = ctype_cts(L); |
| @@ -520,6 +521,30 @@ LJLIB_CF(ffi_abi) LJLIB_REC(.) | |||
| 520 | 521 | ||
| 521 | #undef H_ | 522 | #undef H_ |
| 522 | 523 | ||
| 524 | LJLIB_PUSH(top-7) LJLIB_SET(!) /* Store reference to weak table. */ | ||
| 525 | |||
| 526 | LJLIB_CF(ffi_gc) | ||
| 527 | { | ||
| 528 | GCcdata *cd = ffi_checkcdata(L, 1); | ||
| 529 | TValue *fin = lj_lib_checkany(L, 2); | ||
| 530 | CTState *cts = ctype_cts(L); | ||
| 531 | GCtab *t = cts->finalizer; | ||
| 532 | CType *ct = ctype_raw(cts, cd->typeid); | ||
| 533 | if (!(ctype_isptr(ct->info) || ctype_isstruct(ct->info) || | ||
| 534 | ctype_isrefarray(ct->info))) | ||
| 535 | lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE); | ||
| 536 | if (gcref(t->metatable)) { /* Update finalizer table, if still enabled. */ | ||
| 537 | copyTV(L, lj_tab_set(L, t, L->base), fin); | ||
| 538 | lj_gc_anybarriert(L, t); | ||
| 539 | if (!tvisnil(fin)) | ||
| 540 | cd->marked |= LJ_GC_CDATA_FIN; | ||
| 541 | else | ||
| 542 | cd->marked &= ~LJ_GC_CDATA_FIN; | ||
| 543 | } | ||
| 544 | L->top = L->base+1; /* Pass through the cdata object. */ | ||
| 545 | return 1; | ||
| 546 | } | ||
| 547 | |||
| 523 | LJLIB_PUSH(top-5) LJLIB_SET(!) /* Store clib metatable in func environment. */ | 548 | LJLIB_PUSH(top-5) LJLIB_SET(!) /* Store clib metatable in func environment. */ |
| 524 | 549 | ||
| 525 | LJLIB_CF(ffi_load) | 550 | LJLIB_CF(ffi_load) |
| @@ -538,9 +563,23 @@ LJLIB_PUSH(top-2) LJLIB_SET(arch) | |||
| 538 | 563 | ||
| 539 | /* ------------------------------------------------------------------------ */ | 564 | /* ------------------------------------------------------------------------ */ |
| 540 | 565 | ||
| 566 | /* Create special weak-keyed finalizer table. */ | ||
| 567 | static GCtab *ffi_finalizer(lua_State *L) | ||
| 568 | { | ||
| 569 | /* NOBARRIER: The table is new (marked white). */ | ||
| 570 | GCtab *t = lj_tab_new(L, 0, 1); | ||
| 571 | settabV(L, L->top++, t); | ||
| 572 | setgcref(t->metatable, obj2gco(t)); | ||
| 573 | setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")), | ||
| 574 | lj_str_newlit(L, "K")); | ||
| 575 | t->nomm = (uint8_t)(~(1u<<MM_mode)); | ||
| 576 | return t; | ||
| 577 | } | ||
| 578 | |||
| 541 | LUALIB_API int luaopen_ffi(lua_State *L) | 579 | LUALIB_API int luaopen_ffi(lua_State *L) |
| 542 | { | 580 | { |
| 543 | lj_ctype_init(L); | 581 | CTState *cts = lj_ctype_init(L); |
| 582 | cts->finalizer = ffi_finalizer(L); | ||
| 544 | LJ_LIB_REG(L, NULL, ffi_meta); | 583 | LJ_LIB_REG(L, NULL, ffi_meta); |
| 545 | /* NOBARRIER: basemt is a GC root. */ | 584 | /* NOBARRIER: basemt is a GC root. */ |
| 546 | setgcref(basemt_it(G(L), LJ_TCDATA), obj2gco(tabV(L->top-1))); | 585 | setgcref(basemt_it(G(L), LJ_TCDATA), obj2gco(tabV(L->top-1))); |
diff --git a/src/lj_cdata.c b/src/lj_cdata.c index ae66b4b5..11c84d8e 100644 --- a/src/lj_cdata.c +++ b/src/lj_cdata.c | |||
| @@ -52,7 +52,18 @@ GCcdata *lj_cdata_newv(CTState *cts, CTypeID id, CTSize sz, CTSize align) | |||
| 52 | /* Free a C data object. */ | 52 | /* Free a C data object. */ |
| 53 | void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd) | 53 | void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd) |
| 54 | { | 54 | { |
| 55 | if (LJ_LIKELY(!cdataisv(cd))) { | 55 | if (LJ_UNLIKELY(cd->marked & LJ_GC_CDATA_FIN)) { |
| 56 | GCobj *root; | ||
| 57 | cd->marked = curwhite(g) | LJ_GC_FINALIZED; | ||
| 58 | if ((root = gcref(g->gc.mmudata)) != NULL) { | ||
| 59 | setgcrefr(cd->nextgc, root->gch.nextgc); | ||
| 60 | setgcref(root->gch.nextgc, obj2gco(cd)); | ||
| 61 | setgcref(g->gc.mmudata, obj2gco(cd)); | ||
| 62 | } else { | ||
| 63 | setgcref(cd->nextgc, obj2gco(cd)); | ||
| 64 | setgcref(g->gc.mmudata, obj2gco(cd)); | ||
| 65 | } | ||
| 66 | } else if (LJ_LIKELY(!cdataisv(cd))) { | ||
| 56 | CType *ct = ctype_raw(ctype_ctsG(g), cd->typeid); | 67 | CType *ct = ctype_raw(ctype_ctsG(g), cd->typeid); |
| 57 | CTSize sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_PTR; | 68 | CTSize sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_PTR; |
| 58 | lua_assert(ctype_hassize(ct->info) || ctype_isfunc(ct->info) || | 69 | lua_assert(ctype_hassize(ct->info) || ctype_isfunc(ct->info) || |
diff --git a/src/lj_ctype.c b/src/lj_ctype.c index 956f0a09..ae360b54 100644 --- a/src/lj_ctype.c +++ b/src/lj_ctype.c | |||
| @@ -540,7 +540,7 @@ GCstr *lj_ctype_repr_complex(lua_State *L, void *sp, CTSize size) | |||
| 540 | /* -- C type state -------------------------------------------------------- */ | 540 | /* -- C type state -------------------------------------------------------- */ |
| 541 | 541 | ||
| 542 | /* Initialize C type table and state. */ | 542 | /* Initialize C type table and state. */ |
| 543 | void lj_ctype_init(lua_State *L) | 543 | CTState *lj_ctype_init(lua_State *L) |
| 544 | { | 544 | { |
| 545 | CTState *cts = lj_mem_newt(L, sizeof(CTState), CTState); | 545 | CTState *cts = lj_mem_newt(L, sizeof(CTState), CTState); |
| 546 | CType *ct = lj_mem_newvec(L, CTTYPETAB_MIN, CType); | 546 | CType *ct = lj_mem_newvec(L, CTTYPETAB_MIN, CType); |
| @@ -568,6 +568,7 @@ void lj_ctype_init(lua_State *L) | |||
| 568 | } | 568 | } |
| 569 | } | 569 | } |
| 570 | setmref(G(L)->ctype_state, cts); | 570 | setmref(G(L)->ctype_state, cts); |
| 571 | return cts; | ||
| 571 | } | 572 | } |
| 572 | 573 | ||
| 573 | /* Free C type table and state. */ | 574 | /* Free C type table and state. */ |
diff --git a/src/lj_ctype.h b/src/lj_ctype.h index 555393db..a45767c2 100644 --- a/src/lj_ctype.h +++ b/src/lj_ctype.h | |||
| @@ -158,6 +158,7 @@ typedef struct CTState { | |||
| 158 | MSize sizetab; /* Size of C type table. */ | 158 | MSize sizetab; /* Size of C type table. */ |
| 159 | lua_State *L; /* Lua state (needed for errors and allocations). */ | 159 | lua_State *L; /* Lua state (needed for errors and allocations). */ |
| 160 | global_State *g; /* Global state. */ | 160 | global_State *g; /* Global state. */ |
| 161 | GCtab *finalizer; /* Map of cdata to finalizer. */ | ||
| 161 | CTypeID1 hash[CTHASH_SIZE]; /* Hash anchors for C type table. */ | 162 | CTypeID1 hash[CTHASH_SIZE]; /* Hash anchors for C type table. */ |
| 162 | } CTState; | 163 | } CTState; |
| 163 | 164 | ||
| @@ -428,7 +429,7 @@ LJ_FUNC CTInfo lj_ctype_info(CTState *cts, CTypeID id, CTSize *szp); | |||
| 428 | LJ_FUNC GCstr *lj_ctype_repr(lua_State *L, CTypeID id, GCstr *name); | 429 | LJ_FUNC GCstr *lj_ctype_repr(lua_State *L, CTypeID id, GCstr *name); |
| 429 | LJ_FUNC GCstr *lj_ctype_repr_int64(lua_State *L, uint64_t n, int isunsigned); | 430 | LJ_FUNC GCstr *lj_ctype_repr_int64(lua_State *L, uint64_t n, int isunsigned); |
| 430 | LJ_FUNC GCstr *lj_ctype_repr_complex(lua_State *L, void *sp, CTSize size); | 431 | LJ_FUNC GCstr *lj_ctype_repr_complex(lua_State *L, void *sp, CTSize size); |
| 431 | LJ_FUNC void lj_ctype_init(lua_State *L); | 432 | LJ_FUNC CTState *lj_ctype_init(lua_State *L); |
| 432 | LJ_FUNC void lj_ctype_freestate(global_State *g); | 433 | LJ_FUNC void lj_ctype_freestate(global_State *g); |
| 433 | 434 | ||
| 434 | #endif | 435 | #endif |
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 | { |
diff --git a/src/lj_gc.h b/src/lj_gc.h index 75b38db8..e3973d8e 100644 --- a/src/lj_gc.h +++ b/src/lj_gc.h | |||
| @@ -20,6 +20,7 @@ enum { | |||
| 20 | #define LJ_GC_FINALIZED 0x08 | 20 | #define LJ_GC_FINALIZED 0x08 |
| 21 | #define LJ_GC_WEAKKEY 0x08 | 21 | #define LJ_GC_WEAKKEY 0x08 |
| 22 | #define LJ_GC_WEAKVAL 0x10 | 22 | #define LJ_GC_WEAKVAL 0x10 |
| 23 | #define LJ_GC_CDATA_FIN 0x10 | ||
| 23 | #define LJ_GC_FIXED 0x20 | 24 | #define LJ_GC_FIXED 0x20 |
| 24 | #define LJ_GC_SFIXED 0x40 | 25 | #define LJ_GC_SFIXED 0x40 |
| 25 | 26 | ||
| @@ -42,7 +43,12 @@ enum { | |||
| 42 | 43 | ||
| 43 | /* Collector. */ | 44 | /* Collector. */ |
| 44 | LJ_FUNC size_t lj_gc_separateudata(global_State *g, int all); | 45 | LJ_FUNC size_t lj_gc_separateudata(global_State *g, int all); |
| 45 | LJ_FUNC void lj_gc_finalizeudata(lua_State *L); | 46 | LJ_FUNC void lj_gc_finalize_udata(lua_State *L); |
| 47 | #if LJ_HASFFI | ||
| 48 | LJ_FUNC void lj_gc_finalize_cdata(lua_State *L); | ||
| 49 | #else | ||
| 50 | #define lj_gc_finalize_cdata(L) UNUSED(L) | ||
| 51 | #endif | ||
| 46 | LJ_FUNC void lj_gc_freeall(global_State *g); | 52 | LJ_FUNC void lj_gc_freeall(global_State *g); |
| 47 | LJ_FUNCA int LJ_FASTCALL lj_gc_step(lua_State *L); | 53 | LJ_FUNCA int LJ_FASTCALL lj_gc_step(lua_State *L); |
| 48 | LJ_FUNCA void LJ_FASTCALL lj_gc_step_fixtop(lua_State *L); | 54 | LJ_FUNCA void LJ_FASTCALL lj_gc_step_fixtop(lua_State *L); |
diff --git a/src/lj_state.c b/src/lj_state.c index 85348aa7..11b820b2 100644 --- a/src/lj_state.c +++ b/src/lj_state.c | |||
| @@ -225,7 +225,8 @@ static TValue *cpfinalize(lua_State *L, lua_CFunction dummy, void *ud) | |||
| 225 | { | 225 | { |
| 226 | UNUSED(dummy); | 226 | UNUSED(dummy); |
| 227 | UNUSED(ud); | 227 | UNUSED(ud); |
| 228 | lj_gc_finalizeudata(L); | 228 | lj_gc_finalize_udata(L); |
| 229 | lj_gc_finalize_cdata(L); | ||
| 229 | /* Frame pop omitted. */ | 230 | /* Frame pop omitted. */ |
| 230 | return NULL; | 231 | return NULL; |
| 231 | } | 232 | } |
