packages feed

MicroHs-0.16.0.0: tests/PipeNonBlockIO.hs

module PipeNonBlockIO where

import Control.Monad (void)
import Control.Concurrent
import Control.Concurrent.MVar
import Data.Word
import Foreign.C.Error
import Foreign.C.Types
import Foreign.Marshal.Alloc
import Foreign.Marshal.Array
import Foreign.Ptr
import Foreign.Storable
import System.IO.FD

foreign import ccall "unistd.h pipe"  c_pipe  :: Ptr CInt -> IO CInt
foreign import ccall "unistd.h read"  c_read  :: CInt -> Ptr Word8 -> CInt -> IO CInt
foreign import ccall "unistd.h write" c_write :: CInt -> Ptr Word8 -> CInt -> IO CInt
foreign import ccall "fcntl.h fcntl"  c_fcntl :: CInt -> CInt -> CInt -> IO CInt
foreign import capi "fcntl.h value F_SETFL"    fSETFL    :: CInt
foreign import capi "fcntl.h value O_NONBLOCK" oNONBLOCK :: CInt

setNonBlocking :: CInt -> IO ()
setNonBlocking fd = throwErrnoIfMinus1_ "fcntl" $ c_fcntl fd fSETFL oNONBLOCK

blockOnRead :: CInt -> IO Word8
blockOnRead fd = alloca go
  where
    go p = do
      r <- c_read fd p 1
      if r == 1
        then peek p
        else do
          errno <- getErrno
          if errno == eAGAIN || errno == eWOULDBLOCK
            then do
              putStrLn "entering waitForReadFD"
              waitForReadFD (fromIntegral fd)
              go p
            else throwErrno "read"

reader :: CInt -> MVar () -> IO ()
reader fd done = do
  putStrLn "reader: waiting for data"
  b1 <- blockOnRead fd
  putStrLn $ "reader: received " ++ show b1
  b2 <- blockOnRead fd
  putStrLn $ "reader: received " ++ show b2
  putMVar done ()

pipe :: IO (CInt, CInt)
pipe = do
  allocaArray 2 $ \ fds -> do
    c_pipe fds
    rd <- peekElemOff fds 0
    wr <- peekElemOff fds 1
    return (rd, wr)

main :: IO ()
main = do
  (readFd, writeFd) <- pipe
  setNonBlocking readFd
--  print (readFd, writeFd)

  done <- newEmptyMVar :: IO (MVar ())
  forkIO $ reader readFd done

  threadDelay 1000000 -- let the other thread reach its blocking state

  alloca $ \ p -> do
    putStrLn "main: writing to pipe"
    poke p 42
    c_write writeFd p 1
    threadDelay 1000000
    poke p 43
    c_write writeFd p 1

  takeMVar done