-- thread A
t <- forkIO $ do
_ <- accept listener -- blocks
-- thread B
killThread t
works on Linux (probably also on OS X and FreeBSD) but not on Windows (tried -threaded with +RTS -N4 -RTS etc.).
A
in this case?A
in a special mode that would allow termination at the point it blocks on accept
?A
were forked with forkOS
rather than forkIO
?I noticed this deviant Windows behaviour only when alerted to by a bug report.
Interesting question!
You can't interrupt blocking foreign calls, so I'm somewhat surprised that you're able to interrupt the thread on Linux. Also, forkOS
doesn't help -- that just lets foreign code allocate thread-local storage, but has nothing to do with blocking behavior. But recall that accept can be set to non-blocking:
If no pending connections are present on the queue, and the socket is not marked as nonblocking, accept() blocks the caller until a connection is present. If the socket is marked nonblocking and no pending connections are present on the queue, accept() fails with the error EAGAIN or EWOULDBLOCK.
Which is what is done in the Network library for Posix systems. This then allows the accept
to be interrupted.
An interesting note about Windows:
-- On Windows, our sockets are not put in non-blocking mode (non-blocking
-- is not supported for regular file descriptors on Windows, and it would
-- be a pain to support it only for sockets). So there are two cases:
--
-- - the threaded RTS uses safe calls for socket operations to get
-- non-blocking I/O, just like the rest of the I/O library
--
-- - with the non-threaded RTS, only some operations on sockets will be
-- non-blocking. Reads and writes go through the normal async I/O
-- system. accept() uses asyncDoProc so is non-blocking. A handful
-- of others (recvFrom, sendFd, recvFd) will block all threads - if this
-- is a problem, -threaded is the workaround.
Now, accept on Windows, with the -threaded runtime, uses accept_safe (which allows other threads to make progress) -- but it doesn't put the socket into non-blocking mode:
accept sock@(MkSocket s family stype protocol status) = do
currentStatus <- readMVar status
okay <- sIsAcceptable sock
if not okay
then
ioError (userError ("accept: can't perform accept on socket (" ++ (show (family,stype,protocol)) ++") in status " ++
show currentStatus))
else do
let sz = sizeOfSockAddrByFamily family
allocaBytes sz $ \ sockaddr -> do
#if defined(mingw32_HOST_OS) && defined(__GLASGOW_HASKELL__)
new_sock <-
if threaded
then with (fromIntegral sz) $ \ ptr_len ->
throwErrnoIfMinus1Retry "Network.Socket.accept" $
c_accept_safe s sockaddr ptr_len
else do
paramData <- c_newAcceptParams s (fromIntegral sz) sockaddr
rc <- asyncDoProc c_acceptDoProc paramData
new_sock <- c_acceptNewSock paramData
c_free paramData
when (rc /= 0)
(ioError (errnoToIOError "Network.Socket.accept" (Errno (fromIntegral rc)) Nothing Nothing))
return new_sock
Since 2005, versions of the network
package, on Windows with -threaded explicitly use an accept call marked as safe
, allowing other threads to make progress, but not setting the socket itself into non-blocking mode (so the calling thread blocks).
To work around it I see two options:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With