[Date Prev][Date Next][Thread Prev][Thread Next]
- Subject: Re: Weak tables and userdata finalization
- From: Mark Hamburg <mark@...>
- Date: Sat, 10 Sep 2011 17:38:43 -0700
On Sep 2, 2011, at 8:44 AM, Mark Hamburg wrote:
> I believe I just worked through a bug in my project caused by weak caches together with userdata finalization, but I wanted to confirm that my analysis was correct and not just the result of sleep deprivation.
> * Lua 5.1.4
> * Two userdatas each with __gc metamethods. Let's call them UD1 and UD2.
> * We also have a table T.
> * T references UD2 -- e.g., T.x == UD2
> * T is accessible in a weak cache C (getmetatable( C ).__mode == 'kv') and C[ 'key' ] == T
> * UD1 has an environment that also references T -- e.g., getfenv( UD1 ).y == T
> So, here is what I think is happening to me that I want to confirm is possible.
> * The GC finds that neither UD1 nor UD2 (nor T) are accessible via strong links, so it queues UD1 and UD2 for finalization.
> * As part of queueing UD1 for finalization, it marks its environment so that that remains available for the __gc metamethod and this in turn marks T.
> * T then survives in the cache.
> * We fetch T out of the cache.
> * This doesn't stop UD2 from being finalized which then makes us unhappy when we go to use use UD2 via T.x.
The fix for my code (not yet implemented) and possibly the fix for "your" code too for those of you falling victim to similar patterns: There's no problem that can't be solved with an extra level of indirection...
In this case, we add a fully-weak table to the mix. When finalizing UD1 as it turns out, I need access to the contents of the userdata, but I don't need access to the contents of the environment. So, we change the environment for UD1 to be a fully-weak table leading to the table that was the environment in the old design:
getfenv( UD1 )[ 1 ].y = T
getmetatable( getfenv( UD1 ) ).__mode == 'kv'
Now, when the garbage collector decides to finalize UD1, it will keep getfenv( UD1 ) alive for one more cycle, but this won't keep the old environment table alive and hence won't keep T alive.
So, weak tables look to be part of the problem and part of the solution. Note, however, that this does not work if we need the actual environment table while running the finalizer for UD1.