
Well I'm always happy to take a public pasting in return for the chance to learn something :) It's fair to say I didn't fully understand the implications of TCPs flow-control mechanisms re buffering capacity, and I've been grateful to be guided here.
On 4 Sep 2013, at 16:52, Bryan O'Sullivan
wrote: Your question is actually not related to Haskell at all,
Well.. I'll try to protest my inquiry was specifically targeted at Haskell and that it's reasonable to ask: "what is the behaviour of send when there is no buffering capacity?"; and "as a newcomer to Haskell, broadly what are the mechanisms used to implement send on top of the C API?". Thanks to everyone who provided pointers. Network.Socket has this to say:
Essentially the entire C socket API is exposed through this module; in general the operations follow the behaviour of the C functions of the same name (consult your favourite Unix networking book).
I'm pretty sure you can't understand the behaviour of Network.Socket from consulting a Unix networking book, otherwise you'd be looking for -1 return values and error-codes, and pondering how you might go about efficiently managing read/write to a whole bunch non-blocking sockets. I don't know how one would go about providing a little background in the Network.Socket docs but it seems it would be very helpful; there's no clue provided as to the magic that it's taking care of. Anyway, that's enough hot air from me. I've put my findings below in case anyone else is ever interested. S For anyone interested, here's what I learned from asking questions here and digging around: - The underlying C sockets are in non-blocking mode; - send uses a threadWaitWrite to block the current thread until the socket is writeable; - threadWaitWrite relies on either epoll or kqueue to efficiently identify writeable file-descriptors, therefore select is not required and not implemented by Network.Socket, and there is no need to provide API access to setting O_NONBLOCK; - threadWaitWrite is run inside a throwSocketErrorWaitWrite (Network.Socket.Internal), which uses a throwErrnoIfRetry (Foreign.C.Error); the overall effect is that if the threadWaitWrite/send operation ever errors with EINTR then the whole threadWaitWrite/send is retried; otherwise subsequent sends are managed and called by threadWaitWrite when the send is likely to succeed (which is more efficient than polling using a retry loop alone); all other errors are converted to IOError. The end result on the behaviour of the Network.Socket.send is: - send can result in all sorts of socket-related IOErrors being thrown, except for EINT interrupts because the send is efficiently managed and retried; - send never has a return value of -1, because an exception is raised or the send is retried; - send blocks the current thread if a socket is not currently writeable. - It strikes me that a return value of 0 for Network.Socket.send is unlikely, because otherwise the socket file-descriptor would not have reported that it was writeable and the send would not have been called. But I'm unsure if a 0 value could never be returned. Simon Yarde simonyarde@me.com