[GHC] #13903: KQueue evtmgr backend fails to register for write events

GHC ghc-devs at haskell.org
Fri Jun 30 10:30:35 UTC 2017


#13903: KQueue evtmgr backend fails to register for write events
-------------------------------------+-------------------------------------
           Reporter:  waldheinz      |             Owner:  (none)
               Type:  bug            |            Status:  new
           Priority:  normal         |         Milestone:
          Component:  Runtime        |           Version:  8.0.2
  System                             |
           Keywords:                 |  Operating System:  FreeBSD
       Architecture:                 |   Type of failure:  Incorrect result
  Unknown/Multiple                   |  at runtime
          Test Case:                 |        Blocked By:
           Blocking:                 |   Related Tickets:
Differential Rev(s):                 |         Wiki Page:
-------------------------------------+-------------------------------------
 The root of the problem is that the `GHC.Event.KQueue.toFilter` function
 has type `GHC.Event.Internal.Event -> Filter` with GHC's `Event` being a
 bitmask which can represent read events, write events or a combination of
 those.

 It happens that the event manager requests it's backend to be notified
 about read ''and'' write events on some fd, and because the kqueue
 `EVFILT_*`s are ''not'' bitmasks, the above function cannot capture this,
 silently dropping the desire to be notified about write events.

 The following program triggers the problematic behaviour:

 {{{

 import           Control.Concurrent ( forkIO, killThread )
 import           Control.Exception ( finally )
 import           Control.Monad.Trans.Resource ( runResourceT )
 import           Control.Monad.IO.Class ( liftIO )
 import qualified Data.ByteString as BS
 import           Data.Conduit ( ($$), ($=) )
 import qualified Data.Conduit.Binary as CB
 import qualified Data.Conduit.List as CL
 import qualified Data.Conduit.Network as CN
 import           Data.IORef ( newIORef, modifyIORef', readIORef)

 main :: IO ()
 main = CN.runTCPClient (CN.clientSettings 5555 "192.168.2.11") client
   where
     logMsg cnt = CL.mapM $ \bs -> liftIO $ do
         modifyIORef' cnt (+ 1)
         readIORef cnt >>= \x -> putStrLn $
             "msg #" ++ show x ++  " of size: " ++ show (BS.length bs)
         return bs

     client ad = do
         reader <- forkIO (runResourceT $ CN.appSource ad
             $$ CL.mapM_ ( \bs -> (liftIO . putStrLn) $
                 "read " ++ show (BS.length bs) ++ " bytes"))
         cnt <- newIORef ( 0 :: Int )

         let
             runPipe = runResourceT $ CB.sourceFile "cool-linux-distro.iso"
                 $$ logMsg cnt
                 $= CN.appSink ad

         runPipe `finally` (killThread reader)
 }}}

 Having a `nc -l -p 5555 > /dev/null` running on another machine is
 sufficient to sink the data.

 Assuming that we can read `bigfile.iso` faster than we can send out over
 the socket, the `send` syscall will at some point give an `EAGAIN` as can
 be seen in the `truss` output:

 {{{
 write(1,"msg #20 of size: 32752\n",23)           = 23 (0x17)
 sendto(12,"\f\2409\0\M^RA\^T\M-&A\M-'\M-d8"...,32752,0x0,NULL,0x0) = 32752
 (0x7ff0)
 read(13,"\M^?\0'\\\M-B\M-:+\^]D\M-0\M-="...,32752) = 32752 (0x7ff0)
 poll({ 1/POLLOUT },1,0)                          = 1 (0x1)
 msg #21 of size: 32752
 write(1,"msg #21 of size: 32752\n",23)           = 23 (0x17)
 sendto(12,"\M^?\0'\\\M-B\M-:+\^]D\M-0\M-="...,32752,0x0,NULL,0x0) = 19204
 (0x4b04)
 sendto(12,"\M-j$2\M^BH\M-#-\^A\M-E\^O\M^Y\a"...,13548,0x0,NULL,0x0) ERR#35
 'Resource temporarily unavailable'
 SIGNAL 26 (SIGVTALRM)
 sigprocmask(SIG_SETMASK,{ SIGVTALRM },0x0)       = 0 (0x0)
 sigreturn(0x7fffffff9c60)                        ERR#35 'Resource
 temporarily unavailable'
 kevent(3,{ 12,EVFILT_READ,EV_ADD|EV_ONESHOT,0x0,0x0,0x0 },1,0x0,0,{
 0.000000000 }) = 0 (0x0)
 _umtx_op(0x800c71ed0,UMTX_OP_WAIT_UINT_PRIVATE,0x0,0x0,0x0) ERR#4
 'Interrupted system call'
 SIGNAL 26 (SIGVTALRM)
 sigprocmask(SIG_SETMASK,{ SIGVTALRM },0x0)       = 0 (0x0)
 sigreturn(0x7fffffffdc00)                        ERR#4 'Interrupted system
 call'
 _umtx_op(0x800c71ed0,UMTX_OP_WAIT_UINT_PRIVATE,0x0,0x0,0x0) ERR#4
 'Interrupted system call'
 SIGNAL 26 (SIGVTALRM)
 }}}

 Not the `sendto` call on fd 12 resulting in `ERR#35`, soon followed by an
 `kevent` call for that same fd and only `EVFILT_READ` set. This makes
 little sense as it was an attempt to ''write'' that just failed. This is
 caused by `toFilter` giving precedence to `read` events, dropping the
 write event. Not starting the `reader` thread prevents bad things from
 happening as then the `write` events are properly passed thru to kqueue.

 I have an initial version of a patch fixing this.

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13903>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list