houseman-0.1.0: src/Houseman/Internal.hs
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Houseman.Internal
( terminateAndWaitForProcess
, withAllExit
, withAnyExit
, withProcess
) where
import Control.Concurrent
import Control.Exception
import Control.Monad
import Data.Maybe
import GHC.IO.Exception
import GHC.IO.Handle
import System.IO
import System.Process
import Data.Streaming.Process (Inherited (..), StreamingProcessHandle,
getStreamingProcessExitCode,
streamingProcess,
streamingProcessHandleRaw,
waitForStreamingProcess)
-- | Watch exit of given process handles, and if any of them exited and
-- passed given predicate, runs action.
withAnyExit
:: (ExitCode -> Bool) -- ^ Predicate
-> [StreamingProcessHandle] -- ^ Process handles to watch
-> IO a -- ^ Action to run
-> IO a
withAnyExit predicate phs action = go
where
go = do
exits <- catMaybes <$> mapM getStreamingProcessExitCode phs
if any predicate exits
then action
else threadDelay 1000 >> go
-- | Watch exit of given process handles, and if all of them exited and
-- passed given predicate, runs action.
withAllExit
:: (ExitCode -> Bool) -- ^ Predicate
-> [StreamingProcessHandle] -- ^ Process handles to watch
-> IO a -- ^ Action to run
-> IO a
withAllExit predicate phs action = go
where
go = do
exits <- mapM getStreamingProcessExitCode phs
if all isJust exits && all predicate (catMaybes exits)
then action
else threadDelay 1000 >> go
-- | Terminates and waits for given process.
terminateAndWaitForProcess :: StreamingProcessHandle -> IO ExitCode
terminateAndWaitForProcess ph = do
let ph' = streamingProcessHandleRaw ph
b <- getStreamingProcessExitCode ph
case b of
Just exitcode -> return exitcode
Nothing -> do
terminateProcess ph'
waitForStreamingProcess ph
-- | Runs given process and invoke action with stdout, stderr, process handle from it.
-- stdout and stderr will be closed after action.
withProcess
:: CreateProcess -- ^ The process
-> ((Handle, Handle, StreamingProcessHandle) -> IO a) -- ^ Action takes stdout, stderr and process handle
-> IO a
withProcess p action = bracket before restore action
where
before = do
(Inherited, out :: Handle, err :: Handle, ph) <-
streamingProcess p { std_out = CreatePipe
, std_err = CreatePipe
}
encoding <- mkTextEncoding "utf8"
forM_ [out,err] $ \h -> do
hSetBuffering h NoBuffering
hSetEncoding h encoding
hSetNewlineMode h universalNewlineMode
return (out,err,ph)
restore (out,err,_) = do
close out
close err
close x = do
c <- hIsClosed x
unless c $ hClose x