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でacceptを-threadedランタイムで使用すると、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パッケージのバージョンは、-threadedを使用するWindowsで、としてマークされたaccept呼び出しを明示的に使用し、safe他のスレッドの進行を許可しますが、ソケット自体を非ブロックモードに設定しません(呼び出しスレッドがブロックします)。

それを回避するために、2つのオプションがあります。

  • Windowsでノンブロッキングaccept呼び出しを行う方法を理解し、ネットワークライブラリにパッチを適用します。たとえば、snapやyesodがここで何をするかを見て、すでに解決されているかどうかを確認します。
  • ある種の監視スレッドを使用してepollを偽造し、ブロックされた子スレッドの進行状況を監視します。
于 2012-05-15T21:16:33.597 に答える