9
-- thread A
t <- forkIO $ do
   _ <- accept listener -- blocks

-- thread B
killThread t

适用于 Linux(可能也适用于 OS X 和 FreeBSD)但不适用于 Windows(尝试使用 +RTS -N4 -RTS 等进行线程化)。

  • A在这种情况下终止线程的正确方法是什么?
  • 有没有办法以A特殊模式分叉线程,允许在它阻塞的点终止accept
  • 如果与而不是A分叉会有帮助吗?forkOSforkIO

只有在收到错误报告提醒时,我才注意到这种异常的 Windows 行为。

4

1 回答 1

13

有趣的问题!

你不能中断阻塞的外部调用,所以我有点惊讶你能够中断 Linux 上的线程。此外,forkOS也无济于事——这只是让外部代码分配线程本地存储,但与阻塞行为无关。但请记住,accept 可以设置为非阻塞:

如果队列中没有挂起的连接,并且套接字没有被标记为非阻塞,accept() 会阻塞调用者,直到连接出现。如果套接字被标记为非阻塞并且队列中没有挂起的连接,accept() 将失败并返回 EAGAIN 或 EWOULDBLOCK 错误。

这是在 Posix 系统的网络库中所做的。这然后允许accept被中断。

关于 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.

现在,在 Windows 上接受 -thread 运行时,使用 accept_safe (允许其他线程取得进展)——但它不会将套接字置于非阻塞模式:

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

自 2005 年以来network,在带有 -thread 的 Windows 上的包版本显式使用标记为的接受调用safe,允许其他线程取得进展,但不将套接字本身设置为非阻塞模式(因此调用线程阻塞)。

要解决它,我看到两个选项:

  • 弄清楚如何在 Windows 上进行非阻塞接受调用,并修补网络库——看看 snap 或 yesod 在这里做了什么,看看他们是否已经解决了它。
  • 使用某种监督线程来伪造 epoll,监视阻塞的子线程的进度。
于 2012-05-15T21:16:33.597 回答