lua-users home
lua-l archive

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


This proposed behavior would have no impact oin finalizers:
- they can still use getmetatable(o) and see the unmodiied metatable of the object (no need then for the GC to temporarily set clear it or set it to a dummy (different) metatable.
- they can use setmetatable(o,mt) as they want: no quirk needed in the implementation of setmetatable (whic hthen does not need to care about the fact a finalization of the object is pending)
- all is determined ONLY when the finalizer returns.

A simple finalizer like:
  setmetatable(o, {__gc =function() return true end})
or even just:
  setmetatable(o, __gc =true)
(if we also honor a boolean value of __gc as equivalent to a function returning a boolean) would be enough to say that the object "o" must NEVER be finalized. (the alternative using a boolean would avoid the need to perform any costly calls in repeated attempts to finalize the object, such object would not even be in a finalization list, the GC would then automatically consider the object as "marked", and reachable, the object will never be freed, i.e. will remain permanent in memory)


Le mar. 20 nov. 2018 à 18:42, Philippe Verdy <verdy_p@wanadoo.fr> a écrit :
So in summary, it is the effectively fact that we call "setmetatable(o,mt)" on the object (o) passed in parameter to the finalizer that effectively changes its state (a bit in the object).
In my opinion, this is not the best way to handle it 
- this requires a specific behavior of setmetatable: it inspects the metatatable to see if there's a __gc function in it, then sets the FINALIZEDBIT if it is so, then returns to the finalizer, which may still change the metatable after this.
- the only safe behavior would be that the finalizer **returns** an effective status. For now finalizer are functions that return nothing (or nil), they could return a non-nil value (probably non-false as well, e.g. true) to indicate its desire to indicate that the FINALIZEDBIT must be CLEARED on the object. A finalizer that does nothing (an empty function) would not return anything, so the GC can still safely look at the metadata at this time, see if it has a __gc function, and if not it will set the FINALIZEDBIT (allowing the object to be swept and freed).


Le lun. 19 nov. 2018 à 19:54, Gé Weijers <ge@weijers.org> a écrit :
You'd think that Lua source code is unavailable if you read this thread. The finalizer implementation is fairly simple to understand:
  • if you call 'setmetatable' on an object, and the metatable has a __gc field the object is moved from the 'allgc' list to the 'finobj' list. The FINALIZEDBIT is set in the object's GC state.
  • when the object becomes unreachable it is moved back to the 'allgc' list, and the FINALIZEDBIT is reset. The finalizer is then called.
  • when the object becomes unreachable again (i.e. when it's on the 'allgc' list and there are no references to it) it's freed.
If you call 'setmetatable' in the finalizer, and that metatable has a '__gc' field the object is moved back to the 'finobj' list, so the process starts again and the object is never freed.

Observations:
  • an object is only finalized once, unless you explicitly call 'setmetatable' again to set the finalizer.
  • it's not a bug, the original poster's program explicitly requests that the object be finalized again using 'setmetatable'. The behavior is consistent with the documentation.
  • there are other things you can do in the __gc function, like storing a reference to the object somewhere so it stays reachable. The finalizer has run, but the object will have to be kept around anyway because it just became reachable again. Having the finalizer 'decide' what to do is therefor unsafe. Freeing an object whose finalizer has not run yet requires two GC cycles, at the end of the first one the finalizer runs, at the end of the second one the object is freed.
  • It's not clear to me what the use case is for changing the metatable in a finalizer to begin with. In the normal case you'd expect the object to be unreachable after the finalizer returns, so it will be freed in the next pass.
"It's not a bug, it's a feature!"

--