
Sure enough, after I say that all my fd reaping issues are solved at the Haskell level, I've been bitten by issues that look like the RTS doing things behind my back wrt. reaping fd's etc. etc.
Basically, while I used socketToHandle the fd's got closed prematurely, and once I eliminated all uses of it, the socket fd's appeared to leak despite attempts to explicitly close them.
Also, there were some very strange goings-on with respect to attempts to flush buffers after the fd's they were being flushed to were closed.
So, what are the semantics here?
I wouldn't rule out a bug here. Sockets are actually implemented as two independent handles internally, one each for the read side and write side so that each side gets a separate buffer and duplex reading/writing from different threads is supported. The programmer still just sees a single Handle. The tricky bit is knowing when the socket can be closed: both sides have to be unreferenced by the program. We handle this by having the read side point to the write side, and then place the finalizer on the write side. The finalizer should then only run when both sides are unreferenced (of course, it's always better to do the closing explicitly if you can). ... actually I've just looked at the code and it looks wrong, aargh! The finalizer is attached to the wrong side. If you have a source tree handy, try changing the following line in libraries/base/GHC/Handle.hs: addMVarFinalizer read_side (handleFinalizer read_side) to addMVarFinalizer write_side (handleFinalizer write_side) Cheers, Simon