
Jeremy Shaw wrote:
import Control.Concurrent import Control.Concurrent.MVar import System.Posix.Types
data RW = Read | Write
threadWaitReadWrite :: Fd -> IO RW threadWaitReadWrite fd = do m <- newEmptyMVar rid <- forkIO $ threadWaitRead fd >> putMVar m Read wid <- forkIO $ threadWaitWrite fd >> putMVar m Write r <- takeMVar m killThread rid killThread wid return r
[--snip--]
I've tested this extensively during this weekend and not a single "leaked" FD so far. I think we can safely say that polling an FD for read readiness is sufficient to properly detect a disconnected client regardless of why/how the client disconnected. The only issue I can see with just dropping the above code directly into the sendfile library is that it may lead to busy-waiting on EAGAIN *if* the client is actually trying to send data to the server while it's receiving the file via sendfile(). If the client sends even a single byte and the server isn't reading it from the socket, then threadWaitRead will keep returning immediately since it's level-triggered rather than edge triggered. In the worst case this could be exploited by evil clients as a trivial way to DoS a server -- simply send data while the server is sending you a file. Bam, instant 100% CPU utilization on the server. Not sure what the best solution for this would be, API-wise... Maybe actually have sendfile read the data and supply it to a user-defined function which could react to the data in some way? (Could supply two standard functions: "disconnect immediately" and "accumulate all received data into a bytestring".) Cheers,