|
Am 22.05.2014 08:36 schröbte Tim Hill:
On May 21, 2014, at 11:29 PM, Philipp Janda <siffiejoe@gmx.net> wrote:Yes, that's exactly what I meant with degradation of lifetime granularity. On Lua 5.2 a weak-valued field keeps the reference until the finalizer has run which is what you need, it's just Lua 5.1 that is underspecified in this regard ...It’s then a simple matter to have A call B’s finalizer before it does its own finalization, ensuring B is always done first, assuming of course that’s B’s finalizer is idempotent. This pattern will always work regardless of the behavior of the GC. This can even be generalized by having A maintain a table of references to many B’s.So in the case of my `SDL_Quit` finalizer, it keeps all other SDL objects alive until `lua_close` ...Possibly .. but that depends on how you design object lifetime for your app.. If you want B to be collected independently of A then clearly the A->B reference needs to be weak. I think the point to my mind is that, with the correct combination of strong and weak references, and idempotency, you can create whatever object lifetimes you need without relying on the ordering of finalizers by the GC.
With weak back references you get the problems I already mentioned two posts ago. Anyways, we obviously need some way to manage lifetime dependencies, and the current approach is easy and naturally fits the most common case where one object depends on another for its entire lifetime.
And even if the GC *does* guarantee the ordering, you are then dependent on objects being created in the correct order to ensure they are deleted in the correct order .. that can be a tricky thing to maintain and debug, compared to a design that always does the “right thing” at shutdown.
Or it can be very easy: E.g. require an argument of type A in your B constructor and you know for sure that the A object was created before the B object. For APR and SDL this is also exactly how the C API looks like. Eric's case is quite complicated though ...
Another point, of course, is that using sequences like “objectX = nil; collectgarbage()” can be inefficient compared to explicit Dispose() patterns (GC’s have significant overhead). In this model, the finalizer is just the “last chance” to dispose of the object.
As soon as you have garbage collection and lifetime dependencies, manual disposal doesn't work that well anymore: E.g. if I cleared an APR memory pool (which is possible in the C API), I would break all userdata objects that were created using this memory pool. This is also the reason why exposing something like `SDL_Quit` to Lua is dangerous. Manual management is only possible at the leaf nodes of such a lifetime dependency graph (e.g. I can manually close/delete an APR file object without problems even if it depends on a memory pool).
—Tim
Philipp