gpollLoop :: Logger -> PollMode -> RecvCallback a -> SendCallback a -> IOLoop a -> IO ()
gpollLoop logger mode recvCallback sendCallback p = go
    where
      handleRecv acc
        | length acc == 4 = enqueueS p acc >> handleRecv []
        | otherwise       = do
          mmsg <- useSocket p readMsg
          case mmsg of
            Just msg -> handleRecv (msg : acc)
            Nothing  -> enqueueS p acc
          where
            readMsg fh = do
              zready <- events fh
              if (In `elem` zready)
                then fmap Just $ recvCallback fh
                else return Nothing

      handleSend state = do
        (msg, readStatus) <- dequeue p state
        live              <- readStatus
        rest              <- if live
                               then useSocket p (mySendMsg msg readStatus)
                               else do
                                 warning logger (printf "dropping message [%s#cancel]" (pollName p))
                                 return Nothing
        case rest of
          Nothing -> handleSend rest
          _       -> return rest
          where
            mySendMsg msg ctrl fh = do
              zready <- events fh
              if (Out `elem` zready)
                then sendCallback fh msg >> return Nothing
                else return $ Just (msg, ctrl)

      goWrite fh miss = do
        miss' <- handleSend miss
        case miss' of
          Nothing -> goWrite fh miss'
          _       -> threadWaitWrite fh >> goWrite fh miss'

      goRead fh = do
        threadWaitRead fh
        handleRecv []
        goRead fh

      go = do
        fh  <- useSocket p fileDescriptor
        t0  <- forkIO (when (hasRead mode) $ goRead fh)
        t1  <- forkIO (when (hasWrite mode) $ goWrite fh Nothing)
        readMVar (pollCtrl p)
        mapM_ killThread [t0, t1]
gpollLoop :: Logger -> PollMode -> RecvCallback a -> SendCallback a -> IOLoop a -> IO ()
gpollLoop logger mode recvCallback sendCallback p = go
    where
      handleRecv acc
        | length acc == 4 = enqueueS p acc >> handleRecv []
        | otherwise       = do
          mmsg <- useSocket p readMsg
          case mmsg of
            Just msg -> handleRecv (msg : acc)
            Nothing  -> enqueueS p acc
          where
            readMsg fh = do
              zready <- events fh
              if (In `elem` zready)
                then fmap Just $ recvCallback fh
                else return Nothing

      handleSend state = do
        (msg, readStatus) <- dequeue p state
        live              <- readStatus
        rest              <- if live
                               then useSocket p (mySendMsg msg readStatus)
                               else do
                                 warning logger (printf "dropping message [%s#cancel]" (pollName p))
                                 return Nothing
        case rest of
          Nothing -> handleSend rest
          _       -> return rest
          where
            mySendMsg msg ctrl fh = do
              zready <- events fh
              if (Out `elem` zready)
                then sendCallback fh msg >> return Nothing
                else return $ Just (msg, ctrl)

      goWrite fh miss = do
        miss' <- handleSend miss
        case miss' of
          Nothing -> goWrite fh miss'
          _       -> threadWaitWrite fh >> goWrite fh miss'

      goRead fh = do
        threadWaitRead fh
        handleRecv []
        goRead fh

      go = do
        fh  <- useSocket p fileDescriptor
        t0  <- forkIO (when (hasRead mode) $ goRead fh)
        t1  <- forkIO (when (hasWrite mode) $ goWrite fh Nothing)
        readMVar (pollCtrl p)
        mapM_ killThread [t0, t1]
gpollLoop logger mode recvCallback sendCallback p = go
    where
      handleRecv acc
        | length acc == 4 = enqueueS p acc >> handleRecv []
        | otherwise       = do
          mmsg <- useSocket p readMsg
          case mmsg of
            Just msg -> handleRecv (msg : acc)
            Nothing  -> enqueueS p acc
          where
            readMsg fh = do
              zready <- events fh
              if (In `elem` zready)
                then fmap Just $ recvCallback fh
                else return Nothing

      handleSend state = do
        (msg, readStatus) <- dequeue p state
        live              <- readStatus
        rest              <- if live
                               then useSocket p (mySendMsg msg readStatus)
                               else do
                                 warning logger (printf "dropping message [%s#cancel]" (pollName p))
                                 return Nothing
        case rest of
          Nothing -> handleSend rest
          _       -> return rest
          where
            mySendMsg msg ctrl fh = do
              zready <- events fh
              if (Out `elem` zready)
                then sendCallback fh msg >> return Nothing
                else return $ Just (msg, ctrl)

      goWrite fh miss = do
        miss' <- handleSend miss
        case miss' of
          Nothing -> goWrite fh miss'
          _       -> threadWaitWrite fh >> goWrite fh miss'

      goRead fh = do
        threadWaitRead fh
        handleRecv []
        goRead fh

      go = do
        fh  <- useSocket p fileDescriptor
        t0  <- forkIO (when (hasRead mode) $ goRead fh)
        t1  <- forkIO (when (hasWrite mode) $ goWrite fh Nothing)
        readMVar (pollCtrl p)
        mapM_ killThread [t0, t1]