lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


I know, I know, it is evil thing to do and I agree it is bad idea for
regular code. But hear me out:

local weak = setmetatable({}, {__mode="v"})
local resmt = {A}
function resmt.__gc(v)
  print("resurrecting")
  -- note that re-requesting finalizer is explicit like this
  weak.x = setmetatable(v, resmt)
end
weak.x = setmetatable({}, resmt)
for i=1,10 do 
        collectgarbage()
end
assert(weak.x)

This is the minimal reproduction of problem you'll meet when bridging Lua
with another GC-managed language. When objects are transparently proxied,
finalizers of wrapper proxies on both VMs are relied upon to keep reference
to the other side. Basically to link the two GCs so that either can keep
reference and object is collected only if references are lost in both
worlds. One must carefully keep GCs in sync using finalizers and resurrection.

But this does not work with Lua, because Lua calls finalizer only once, after
that it leaks/crashes because it appears the Lua VM holds reference to the
proxy userdata, while in fact it does not (it just didn't call the finalizer
again after resurrection).

Creating new proxy for each reference to same object is impractical, as there
is high traffic across the bridge - every property access would have to
create unique userdata object.

The following patch appears to work, but it most likely introduces bugs into
GC, as I really don't have idea what I am doing. Safer approach would be
probably to introduce another flag set in GCTM and make luaC_checkfinalizer
aware of it, to avoid unwarranted side-effects wrt other parts of GC which
deal with finalized data separation and check for isfinalized.

diff -ruN lua-5.3.0-work2/src/lgc.c lua-5.3.0-work2-resurrectfin/src/lgc.c
--- lua-5.3.0-work2/src/lgc.c 2014-03-21 14:52:33.000000000 +0100
+++ lua-5.3.0-work2-resurrectfin/src/lgc.c  2014-05-03 17:53:35.358176913 +0200
@@ -815,6 +815,7 @@
     int running  = g->gcrunning;
     L->allowhook = 0;  /* stop debug hooks during GC metamethod */
     g->gcrunning = 0;  /* avoid GC steps */
+    resetbit(gch(gcvalue(&v))->marked, FINALIZEDBIT); /* Might be finalized again. */
     setobj2s(L, L->top, tm);  /* push finalizer... */
     setobj2s(L, L->top + 1, &v);  /* ... and its argument */
     L->top += 2;  /* and (next line) call the finalizer */

Attachment: multifinalizer.patch
Description: Binary data