When an object is moved to a different Ractor, the finalizers are not copied to the new object, so it will have the FL_FINALIZE flag set but no entry in the finalizer table.
r=Ractor.newdoRactor.receive# don't bind to a variable (let it be garbage collected)GC.startendo=Object.newObjectSpace.define_finalizer(o,proc{|id|})r.send(o,move: true)r.take
I have attached an updated version of @zzak (zzak _)_'s patch which resolves this crash, but I suppose this is inappropiate since this will leak outer variables in the following case:
r=Ractor.newdoRactor.receiveGC.start# `unshareable` happens to get read in this Ractorendo=Object.newunshareable=+"hello"ObjectSpace.define_finalizer(o,proc{|id|punshareable})r.send(o,move: true)r.take
Maybe simply marking objects with a finalizer ineligible for moving is more appropiate.
Maybe simply marking objects with a finalizer ineligible for moving is more appropiate.
Thanks for checking, I've updated the PR to raise if the object has a finalizer. I'm not sure if we should do the check in like make_shareable_check_shareable instead, for example.
Keep FL_FINALIZE on the shell an object leaves behind when it is moved
move_neutralize_source() rewrites the source's flags to
T_OBJECT | FL_FREEZE | (flags & FL_PROMOTED), which drops FL_FINALIZE
while the finalizer table entry keyed on that slot stays. The two then
disagree, and a RUBY_DEBUG build aborts at shutdown:
Carry FL_FINALIZE over to the shell. The finalizer then runs when the
shell is collected, in the Ractor that defined it; the object rebuilt on
the other side gets fresh flags and does not inherit it, so it still runs
exactly once.