
Attaching a finalizer to a regular data type like that is a bit of a hazard
prone process. If GHC unpack your Socket in another data constructor it
will get "freed" and then your socket can get finalized while you still
have access to the CInt!
data MySockets = MySockets {-# unpack #-} !Socket {-# unpack #-} !Socket
will happily 'eat' your data constructor wrapper and if there are no
references remaining to a copy of it on the heap, their finalizers will
fire. Worker-wrapper transforms can do this even without any "containing"
data type if you just increase your optimization level!
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
-Edward
On Tue, Jan 30, 2018 at 12:18 AM, Kazu Yamamoto
Hi,
I registered this issue to network:
https://github.com/haskell/network/issues/302
Viktor, thank you!
--Kazu
On Jan 29, 2018, at 10:25 PM, Kazu Yamamoto (山本和彦)
wrote: socket family stype protocol = do fd <- c_socket ... ... let s = Socket fd addFinalizer s $ close s ruturn s
For the record, I think I've convinced Kazu Yamamoto that this is an anti-pattern. Such a finalizer would easily end up closing already closed sockets, whose file-descriptors may already be associated with other open files or sockets. That way lie all sorts of difficult to isolate race-condition bugs. To make this safe, the close function would need to mutate the socket, invalidating the enclosed file-descriptor, and would then need to be a NOP or just raise an exception if the socket is closed again (the finalizer should invoke a close variant that just returns without raising exceptions if the socket is already closed).
There is, AFAIK still an unresolved bug along these lines somewhere in http-client and its dependencies. So far no reproducing cases have been provided. No very recent reports either, perhaps it went away, or people have just been more lucky lately:
https://github.com/snoyberg/http-client/issues/252 https://github.com/vincenthz/hs-tls/issues/179
All that said, the original question about addFinalizer vs. GHC 7.10 may still be worth exploring, even if its use-case for Sockets goes away. So, please don't take this poset to mean that the original question should be ignored.
-- 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.
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.