aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pall <mike>2011-02-28 16:48:13 +0100
committerMike Pall <mike>2011-02-28 16:48:13 +0100
commit83a37aeca74724ef76dee7c8246bdbb88132940d (patch)
tree9dca0cd0aa13cf911ad26c3655533a72bb6790a9
parentcead25f928ac606fc1a13882b818913aab3635a9 (diff)
downloadluajit-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.html28
-rw-r--r--src/Makefile.dep10
-rw-r--r--src/lib_ffi.c77
-rw-r--r--src/lj_cdata.c13
-rw-r--r--src/lj_ctype.c3
-rw-r--r--src/lj_ctype.h3
-rw-r--r--src/lj_gc.c103
-rw-r--r--src/lj_gc.h8
-rw-r--r--src/lj_state.c3
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
238checks or to convert pointers to addresses or vice versa. 238checks 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>
243Associates a finalizer with a pointer or aggregate cdata object. The
244cdata object is returned unchanged.
245</p>
246<p>
247This function allows safe integration of unmanaged resources into the
248automatic memory management of the LuaJIT garbage collector. Typical
249usage:
250</p>
251<pre class="code">
252local p = ffi.gc(ffi.C.malloc(n), ffi.C.free)
253...
254p = nil -- Last reference to p is gone.
255-- GC will eventually run finalizer: ffi.C.free(p)
256</pre>
257<p>
258A cdata finalizer works like the <tt>__gc</tt> metamethod for userdata
259objects: when the last reference to a cdata object is gone, the
260associated finalizer is called with the cdata object as an argument. The
261finalizer can be a Lua function or a cdata function or cdata function
262pointer. An existing finalizer can be removed by setting a <tt>nil</tt>
263finalizer, e.g. right before explicitly deleting a resource:
264</p>
265<pre class="code">
266ffi.C.free(ffi.gc(p, nil)) -- Manually free the memory.
267</pre>
268
241<h2 id="info">C&nbsp;Type Information</h2> 269<h2 id="info">C&nbsp;Type Information</h2>
242<p> 270<p>
243The following API functions return information about C&nbsp;types. 271The following API functions return information about C&nbsp;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 \
21lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ 21lib_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
23lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ 23lib_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
27lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h 27lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h
28lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ 28lib_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
97lj_gc.o: lj_gc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ 97lj_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
101lj_gdbjit.o: lj_gdbjit.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ 101lj_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
162ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \ 162ljamalg.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
357LJLIB_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
356LJLIB_CF(ffi_typeof) 375LJLIB_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
422LJLIB_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
440LJLIB_CF(ffi_string) LJLIB_REC(.) 441LJLIB_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
524LJLIB_PUSH(top-7) LJLIB_SET(!) /* Store reference to weak table. */
525
526LJLIB_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
523LJLIB_PUSH(top-5) LJLIB_SET(!) /* Store clib metatable in func environment. */ 548LJLIB_PUSH(top-5) LJLIB_SET(!) /* Store clib metatable in func environment. */
524 549
525LJLIB_CF(ffi_load) 550LJLIB_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. */
567static 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
541LUALIB_API int luaopen_ffi(lua_State *L) 579LUALIB_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. */
53void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd) 53void 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. */
543void lj_ctype_init(lua_State *L) 543CTState *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);
428LJ_FUNC GCstr *lj_ctype_repr(lua_State *L, CTypeID id, GCstr *name); 429LJ_FUNC GCstr *lj_ctype_repr(lua_State *L, CTypeID id, GCstr *name);
429LJ_FUNC GCstr *lj_ctype_repr_int64(lua_State *L, uint64_t n, int isunsigned); 430LJ_FUNC GCstr *lj_ctype_repr_int64(lua_State *L, uint64_t n, int isunsigned);
430LJ_FUNC GCstr *lj_ctype_repr_complex(lua_State *L, void *sp, CTSize size); 431LJ_FUNC GCstr *lj_ctype_repr_complex(lua_State *L, void *sp, CTSize size);
431LJ_FUNC void lj_ctype_init(lua_State *L); 432LJ_FUNC CTState *lj_ctype_init(lua_State *L);
432LJ_FUNC void lj_ctype_freestate(global_State *g); 433LJ_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. */
464static 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. */
462static void gc_finalize(lua_State *L) 487static 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. */
502void lj_gc_finalizeudata(lua_State *L) 525void 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. */
533void 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. */
509void lj_gc_freeall(global_State *g) 556void 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. */
44LJ_FUNC size_t lj_gc_separateudata(global_State *g, int all); 45LJ_FUNC size_t lj_gc_separateudata(global_State *g, int all);
45LJ_FUNC void lj_gc_finalizeudata(lua_State *L); 46LJ_FUNC void lj_gc_finalize_udata(lua_State *L);
47#if LJ_HASFFI
48LJ_FUNC void lj_gc_finalize_cdata(lua_State *L);
49#else
50#define lj_gc_finalize_cdata(L) UNUSED(L)
51#endif
46LJ_FUNC void lj_gc_freeall(global_State *g); 52LJ_FUNC void lj_gc_freeall(global_State *g);
47LJ_FUNCA int LJ_FASTCALL lj_gc_step(lua_State *L); 53LJ_FUNCA int LJ_FASTCALL lj_gc_step(lua_State *L);
48LJ_FUNCA void LJ_FASTCALL lj_gc_step_fixtop(lua_State *L); 54LJ_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}