[Haskell-cafe] addFinalizer in GHC 7.10
Kazu Yamamoto (=?iso-2022-jp?B?GyRCOzNLXE9CSScbKEI=?=)
kazu at iij.ad.jp
Wed Jan 31 00:51:20 UTC 2018
Hi Edward,
Thank you for your excellent explanation!
I'm now being convinced by the IORef approach.
I should write PoC for that.
--Kazu
> Yes, this can happen even if you're opaque and don't export stuff.
>
> You could attach the finalizer to an (unpacked) IORef that holds the Bool
> that says whether you've been finalized. An IORef holds onto a MutVar#
> under the hood, and so it also maintains the same sort of stable presence
> Weak# offered above. Similarly you _could_ just stuff the info in an MVar
> or ForeignPtr. Each of those has support for attaching a finalizer directly
> to the heap allocated part that lives in # and therefore isn't vulnerable
> to being inlined or unpacked.
>
> There probably should be able to be similar options for attaching the
> finalizer to a MutableByteArray#, even if the combinators don't exist right
> now, etc.
>
> I'd personally go with an IORef and then atomicModifyIORef during the
> "close" and "finalize" operations to extract and set the flag in one
> operation. That way there isn't any communication overhead, but IORef,
> ForeignPtr, MVar, etc. could all work.
>
> -Edward
>
> On Tue, Jan 30, 2018 at 5:09 PM, Viktor Dukhovni <ietf-dane at dukhovni.org>
> wrote:
>
>>
>>
>> > On Jan 30, 2018, at 4:50 PM, Edward Kmett <ekmett at gmail.com> wrote:
>> >
>> > It would be much, much safer to attach the finalizer to something that
>> has a "presence" all its own, like Weak# () as is done in ForeignPtr. This
>> would result in something like:
>> >
>> > data Socket = Socket !CInt (Weak# ())
>> >
>> > Then when it gets unpacked into another data constructor, then the Weak#
>> () still exists. This isn't free, it comes at the cost that your sockets
>> take a couple of words each (plus finalizer space), but the approach you
>> are taking now isn't free either as it isn't really sound. ;)
>> >
>> > tl;dr don't attach finalizers to regular Haskell data types if you can
>> help it
>>
>> THanks, good to know. I gather the unpacking can/will happen even if
>> Socket internals are [made] opaque to other modules?
>>
>> And of course in this case, in addition to avoiding
>> running the finalizer too early, it is critical that each socket be closed
>> at most once. Therefore, to support finalization, and make the API safe
>> for multiple close (as seems to be the case with System.IO Handle's for
>> example) there's a need for additional mutable state in the Socket, to
>> keep track of whether it has or has not yet been closed.
>>
>> I am curious as to what your suggestion would be as to how to best keep
>> track of such state.
>>
>> 1. Employ a separate MVar to keep track of socket state, and update
>> it on close to ensure at most once close.
>>
>> 2. Wrap the file descriptor in an IORef, and set it to an invalid
>> value (-1 on Unix, INVALID_SOCKET on Windows) on close. This
>> avoids misuse not only with close, but also with attempts at
>> read/write/... I/O after close. However it does not avoid
>> (far less likely I think) races to close the socket from
>> multiple threads.
>>
>> 3. Move the state to a wrapper structure managed in FFI code
>> so that all socket operations are via a foreign pointer to
>> a C-structure in which the file descriptor is invalidated on
>> close.
>>
>> 4. Other suggestions...
>>
>> --
>> Viktor.
>>
>> _______________________________________________
>> Haskell-Cafe mailing list
>> To (un)subscribe, modify options or view archives go to:
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
>> Only members subscribed via the mailman list are allowed to post.
>>
More information about the Haskell-Cafe
mailing list