module ToySolver.Internal.ProcessUtil
( runProcessWithOutputCallback
) where
import Control.Concurrent
import Control.Concurrent.STM
import Control.Exception (SomeException, try, mask, throwIO)
import qualified Control.Exception as C
import Control.Monad
import Foreign.C
import System.Exit
import System.IO
#if MIN_VERSION_base(4,6,0)
import System.IO.Error
#else
import System.IO.Error hiding (try)
#endif
import System.Process
#ifdef __GLASGOW_HASKELL__
import GHC.IO.Exception ( IOErrorType(..), IOException(..) )
#endif
runProcessWithOutputCallback
:: FilePath
-> [String]
-> String
-> (String -> IO ())
-> (String -> IO ())
-> IO ExitCode
runProcessWithOutputCallback cmd args input putMsg putErr = do
(Just inh, Just outh, Just errh, processh) <- createProcess
(proc cmd args)
{ std_in = CreatePipe
, std_out = CreatePipe
, std_err = CreatePipe
}
req <- newEmptyTMVarIO
let f act = atomically (putTMVar req act)
m1 = forever (hGetLine outh >>= \s -> f (putMsg s))
`catchIOError` (\e -> if isEOFError e then return () else ioError e)
m2 = forever (hGetLine errh >>= \s -> f (putErr s))
`catchIOError` (\e -> if isEOFError e then return () else ioError e)
withForkWait m1 $ \waitOut -> do
withForkWait m2 $ \waitErr -> do
unless (null input) $
ignoreSigPipe $ hPutStr inh input
ignoreSigPipe $ hClose inh
hSetBuffering outh LineBuffering
hSetBuffering errh LineBuffering
let loop = join $ atomically $ msum $
[ do act <- takeTMVar req
return $ act >> loop
, do guard =<< isEmptyTMVar req
waitOut
waitErr
return $ return ()
]
loop
hClose outh
hClose errh
waitForProcess processh
withForkWait :: IO () -> (STM () -> IO a) -> IO a
withForkWait async body = do
waitVar <- newEmptyTMVarIO :: IO (TMVar (Either SomeException ()))
mask $ \restore -> do
tid <- forkIO $ try (restore async) >>= \v -> atomically (putTMVar waitVar v)
let wait = takeTMVar waitVar >>= either throwSTM return
restore (body wait) `C.onException` killThread tid
ignoreSigPipe :: IO () -> IO ()
#if defined(__GLASGOW_HASKELL__)
ignoreSigPipe = C.handle $ \e -> case e of
IOError { ioe_type = ResourceVanished
, ioe_errno = Just ioe }
| Errno ioe == ePIPE -> return ()
_ -> throwIO e
#else
ignoreSigPipe = id
#endif