{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Reflex.Process
( createProcess
, createProcessBufferingInput
, defProcessConfig
, unsafeCreateProcessWithHandles
, Process(..)
, ProcessConfig(..)
, SendPipe (..)
, createRedirectedProcess
) where
import Control.Concurrent.Async (Async, async, waitBoth)
import Control.Concurrent.Chan (newChan, readChan, writeChan)
import Control.Exception (finally)
import Control.Monad (void, when)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Default (Default, def)
import Data.Function (fix)
import Data.Traversable (for)
import GHC.IO.Handle (Handle)
import qualified GHC.IO.Handle as H
import System.Exit (ExitCode)
import qualified System.Posix.Signals as P
import System.Process hiding (createProcess)
import qualified System.Process as P
import Reflex
data SendPipe i
= SendPipe_Message i
| SendPipe_EOF
| SendPipe_LastMessage i
data ProcessConfig t i = ProcessConfig
{ ProcessConfig t i -> Event t i
_processConfig_stdin :: Event t i
, ProcessConfig t i -> Event t Signal
_processConfig_signal :: Event t P.Signal
}
instance Reflex t => Default (ProcessConfig t i) where
def :: ProcessConfig t i
def = ProcessConfig t i
forall t i. Reflex t => ProcessConfig t i
defProcessConfig
defProcessConfig :: Reflex t => ProcessConfig t i
defProcessConfig :: ProcessConfig t i
defProcessConfig = Event t i -> Event t Signal -> ProcessConfig t i
forall t i. Event t i -> Event t Signal -> ProcessConfig t i
ProcessConfig Event t i
forall k (t :: k) a. Reflex t => Event t a
never Event t Signal
forall k (t :: k) a. Reflex t => Event t a
never
data Process t o e = Process
{ Process t o e -> ProcessHandle
_process_handle :: P.ProcessHandle
, Process t o e -> Event t o
_process_stdout :: Event t o
, Process t o e -> Event t e
_process_stderr :: Event t e
, Process t o e -> Event t ExitCode
_process_exit :: Event t ExitCode
, Process t o e -> Event t Signal
_process_signal :: Event t P.Signal
}
createProcess
:: (MonadIO m, TriggerEvent t m, PerformEvent t m, MonadIO (Performable m))
=> P.CreateProcess
-> ProcessConfig t (SendPipe ByteString)
-> m (Process t ByteString ByteString)
createProcess :: CreateProcess
-> ProcessConfig t (SendPipe ByteString)
-> m (Process t ByteString ByteString)
createProcess p :: CreateProcess
p procConfig :: ProcessConfig t (SendPipe ByteString)
procConfig = do
Chan (SendPipe ByteString)
channel <- IO (Chan (SendPipe ByteString)) -> m (Chan (SendPipe ByteString))
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO (Chan (SendPipe ByteString))
forall a. IO (Chan a)
newChan
IO (SendPipe ByteString)
-> (SendPipe ByteString -> IO ())
-> CreateProcess
-> ProcessConfig t (SendPipe ByteString)
-> m (Process t ByteString ByteString)
forall (m :: * -> *) t.
(MonadIO m, TriggerEvent t m, PerformEvent t m,
MonadIO (Performable m)) =>
IO (SendPipe ByteString)
-> (SendPipe ByteString -> IO ())
-> CreateProcess
-> ProcessConfig t (SendPipe ByteString)
-> m (Process t ByteString ByteString)
createProcessBufferingInput (Chan (SendPipe ByteString) -> IO (SendPipe ByteString)
forall a. Chan a -> IO a
readChan Chan (SendPipe ByteString)
channel) (Chan (SendPipe ByteString) -> SendPipe ByteString -> IO ()
forall a. Chan a -> a -> IO ()
writeChan Chan (SendPipe ByteString)
channel) CreateProcess
p ProcessConfig t (SendPipe ByteString)
procConfig
createProcessBufferingInput
:: (MonadIO m, TriggerEvent t m, PerformEvent t m, MonadIO (Performable m))
=> IO (SendPipe ByteString)
-> (SendPipe ByteString -> IO ())
-> P.CreateProcess
-> ProcessConfig t (SendPipe ByteString)
-> m (Process t ByteString ByteString)
createProcessBufferingInput :: IO (SendPipe ByteString)
-> (SendPipe ByteString -> IO ())
-> CreateProcess
-> ProcessConfig t (SendPipe ByteString)
-> m (Process t ByteString ByteString)
createProcessBufferingInput readBuffer :: IO (SendPipe ByteString)
readBuffer writeBuffer :: SendPipe ByteString -> IO ()
writeBuffer = (Handle -> IO (SendPipe ByteString -> IO ()))
-> (Handle -> (ByteString -> IO ()) -> IO (IO ()))
-> (Handle -> (ByteString -> IO ()) -> IO (IO ()))
-> CreateProcess
-> ProcessConfig t (SendPipe ByteString)
-> m (Process t ByteString ByteString)
forall t (m :: * -> *) i o e.
(MonadIO m, TriggerEvent t m, PerformEvent t m,
MonadIO (Performable m)) =>
(Handle -> IO (i -> IO ()))
-> (Handle -> (o -> IO ()) -> IO (IO ()))
-> (Handle -> (e -> IO ()) -> IO (IO ()))
-> CreateProcess
-> ProcessConfig t i
-> m (Process t o e)
unsafeCreateProcessWithHandles Handle -> IO (SendPipe ByteString -> IO ())
input Handle -> (ByteString -> IO ()) -> IO (IO ())
forall a. Handle -> (ByteString -> IO a) -> IO (IO ())
output Handle -> (ByteString -> IO ()) -> IO (IO ())
forall a. Handle -> (ByteString -> IO a) -> IO (IO ())
output
where
input :: Handle -> IO (SendPipe ByteString -> IO ())
input :: Handle -> IO (SendPipe ByteString -> IO ())
input h :: Handle
h = do
Handle -> BufferMode -> IO ()
H.hSetBuffering Handle
h BufferMode
H.NoBuffering
IO (Async ()) -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO (Async ()) -> IO ()) -> IO (Async ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ IO (Async ()) -> IO (Async ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Async ()) -> IO (Async ())) -> IO (Async ()) -> IO (Async ())
forall a b. (a -> b) -> a -> b
$ IO () -> IO (Async ())
forall a. IO a -> IO (Async a)
async (IO () -> IO (Async ())) -> IO () -> IO (Async ())
forall a b. (a -> b) -> a -> b
$ (IO () -> IO ()) -> IO ()
forall a. (a -> a) -> a
fix ((IO () -> IO ()) -> IO ()) -> (IO () -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \loop :: IO ()
loop -> do
SendPipe ByteString
newMessage <- IO (SendPipe ByteString)
readBuffer
Bool
open <- Handle -> IO Bool
H.hIsOpen Handle
h
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
open (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Bool
writable <- Handle -> IO Bool
H.hIsWritable Handle
h
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
writable (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
case SendPipe ByteString
newMessage of
SendPipe_Message m :: ByteString
m -> Handle -> ByteString -> IO ()
BS.hPutStr Handle
h ByteString
m
SendPipe_LastMessage m :: ByteString
m -> Handle -> ByteString -> IO ()
BS.hPutStr Handle
h ByteString
m IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Handle -> IO ()
H.hClose Handle
h
SendPipe_EOF -> Handle -> IO ()
H.hClose Handle
h
IO ()
loop
(SendPipe ByteString -> IO ()) -> IO (SendPipe ByteString -> IO ())
forall (m :: * -> *) a. Monad m => a -> m a
return SendPipe ByteString -> IO ()
writeBuffer
output :: Handle -> (ByteString -> IO a) -> IO (IO ())
output h :: Handle
h trigger :: ByteString -> IO a
trigger = do
Handle -> BufferMode -> IO ()
H.hSetBuffering Handle
h BufferMode
H.LineBuffering
IO () -> IO (IO ())
forall (f :: * -> *) a. Applicative f => a -> f a
pure (IO () -> IO (IO ())) -> IO () -> IO (IO ())
forall a b. (a -> b) -> a -> b
$ (IO () -> IO ()) -> IO ()
forall a. (a -> a) -> a
fix ((IO () -> IO ()) -> IO ()) -> (IO () -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \go :: IO ()
go -> do
Bool
open <- Handle -> IO Bool
H.hIsOpen Handle
h
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
open (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
Bool
readable <- Handle -> IO Bool
H.hIsReadable Handle
h
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
readable (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
ByteString
out <- Handle -> Int -> IO ByteString
BS.hGetSome Handle
h 32768
if ByteString -> Bool
BS.null ByteString
out
then Handle -> IO ()
H.hClose Handle
h
else IO a -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (ByteString -> IO a
trigger ByteString
out) IO () -> IO () -> IO ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> IO ()
go
unsafeCreateProcessWithHandles
:: forall t m i o e. (MonadIO m, TriggerEvent t m, PerformEvent t m, MonadIO (Performable m))
=> (Handle -> IO (i -> IO ()))
-> (Handle -> (o -> IO ()) -> IO (IO ()))
-> (Handle -> (e -> IO ()) -> IO (IO ()))
-> P.CreateProcess
-> ProcessConfig t i
-> m (Process t o e)
unsafeCreateProcessWithHandles :: (Handle -> IO (i -> IO ()))
-> (Handle -> (o -> IO ()) -> IO (IO ()))
-> (Handle -> (e -> IO ()) -> IO (IO ()))
-> CreateProcess
-> ProcessConfig t i
-> m (Process t o e)
unsafeCreateProcessWithHandles mkWriteStdInput :: Handle -> IO (i -> IO ())
mkWriteStdInput mkReadStdOutput :: Handle -> (o -> IO ()) -> IO (IO ())
mkReadStdOutput mkReadStdError :: Handle -> (e -> IO ()) -> IO (IO ())
mkReadStdError p :: CreateProcess
p (ProcessConfig input :: Event t i
input signal :: Event t Signal
signal) = do
(Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
po <- IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
-> m (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
-> m (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle))
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
-> m (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
forall a b. (a -> b) -> a -> b
$ CreateProcess
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
P.createProcess CreateProcess
p { std_in :: StdStream
std_in = StdStream
P.CreatePipe, std_out :: StdStream
std_out = StdStream
P.CreatePipe, std_err :: StdStream
std_err = StdStream
P.CreatePipe }
(hIn :: Handle
hIn, hOut :: Handle
hOut, hErr :: Handle
hErr, ph :: ProcessHandle
ph) <- case (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
po of
(Just hIn :: Handle
hIn, Just hOut :: Handle
hOut, Just hErr :: Handle
hErr, ph :: ProcessHandle
ph) -> (Handle, Handle, Handle, ProcessHandle)
-> m (Handle, Handle, Handle, ProcessHandle)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Handle
hIn, Handle
hOut, Handle
hErr, ProcessHandle
ph)
_ -> [Char] -> m (Handle, Handle, Handle, ProcessHandle)
forall a. HasCallStack => [Char] -> a
error "Reflex.Process.unsafeCreateProcessWithHandles: Created pipes were not returned by System.Process.createProcess."
i -> IO ()
writeInput :: i -> IO () <- IO (i -> IO ()) -> m (i -> IO ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (i -> IO ()) -> m (i -> IO ()))
-> IO (i -> IO ()) -> m (i -> IO ())
forall a b. (a -> b) -> a -> b
$ Handle -> IO (i -> IO ())
mkWriteStdInput Handle
hIn
Event t (Performable m ()) -> m ()
forall t (m :: * -> *).
PerformEvent t m =>
Event t (Performable m ()) -> m ()
performEvent_ (Event t (Performable m ()) -> m ())
-> Event t (Performable m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ IO () -> Performable m ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> Performable m ())
-> (i -> IO ()) -> i -> Performable m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. i -> IO ()
writeInput (i -> Performable m ()) -> Event t i -> Event t (Performable m ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Event t i
input
Event t (Maybe Signal)
sigOut :: Event t (Maybe P.Signal) <- Event t (Performable m (Maybe Signal))
-> m (Event t (Maybe Signal))
forall t (m :: * -> *) a.
PerformEvent t m =>
Event t (Performable m a) -> m (Event t a)
performEvent (Event t (Performable m (Maybe Signal))
-> m (Event t (Maybe Signal)))
-> Event t (Performable m (Maybe Signal))
-> m (Event t (Maybe Signal))
forall a b. (a -> b) -> a -> b
$ Event t Signal
-> (Signal -> Performable m (Maybe Signal))
-> Event t (Performable m (Maybe Signal))
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
ffor Event t Signal
signal ((Signal -> Performable m (Maybe Signal))
-> Event t (Performable m (Maybe Signal)))
-> (Signal -> Performable m (Maybe Signal))
-> Event t (Performable m (Maybe Signal))
forall a b. (a -> b) -> a -> b
$ \sig :: Signal
sig -> IO (Maybe Signal) -> Performable m (Maybe Signal)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe Signal) -> Performable m (Maybe Signal))
-> IO (Maybe Signal) -> Performable m (Maybe Signal)
forall a b. (a -> b) -> a -> b
$ do
Maybe Pid
mpid <- ProcessHandle -> IO (Maybe Pid)
P.getPid ProcessHandle
ph
Maybe Pid -> (Pid -> IO Signal) -> IO (Maybe Signal)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
t a -> (a -> f b) -> f (t b)
for Maybe Pid
mpid ((Pid -> IO Signal) -> IO (Maybe Signal))
-> (Pid -> IO Signal) -> IO (Maybe Signal)
forall a b. (a -> b) -> a -> b
$ \pid :: Pid
pid -> Signal
sig Signal -> IO () -> IO Signal
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Signal -> Pid -> IO ()
P.signalProcess Signal
sig Pid
pid
let
output :: Handle -> m (Event t o, Async ())
output :: Handle -> m (Event t o, Async ())
output h :: Handle
h = do
(e :: Event t o
e, trigger :: o -> IO ()
trigger) <- m (Event t o, o -> IO ())
forall t (m :: * -> *) a.
TriggerEvent t m =>
m (Event t a, a -> IO ())
newTriggerEvent
IO ()
reader <- IO (IO ()) -> m (IO ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (IO ()) -> m (IO ())) -> IO (IO ()) -> m (IO ())
forall a b. (a -> b) -> a -> b
$ Handle -> (o -> IO ()) -> IO (IO ())
mkReadStdOutput Handle
h o -> IO ()
trigger
Async ()
t <- IO (Async ()) -> m (Async ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Async ()) -> m (Async ())) -> IO (Async ()) -> m (Async ())
forall a b. (a -> b) -> a -> b
$ IO () -> IO (Async ())
forall a. IO a -> IO (Async a)
async IO ()
reader
(Event t o, Async ()) -> m (Event t o, Async ())
forall (m :: * -> *) a. Monad m => a -> m a
return (Event t o
e, Async ()
t)
errOutput :: Handle -> m (Event t e, Async ())
errOutput :: Handle -> m (Event t e, Async ())
errOutput h :: Handle
h = do
(e :: Event t e
e, trigger :: e -> IO ()
trigger) <- m (Event t e, e -> IO ())
forall t (m :: * -> *) a.
TriggerEvent t m =>
m (Event t a, a -> IO ())
newTriggerEvent
IO ()
reader <- IO (IO ()) -> m (IO ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (IO ()) -> m (IO ())) -> IO (IO ()) -> m (IO ())
forall a b. (a -> b) -> a -> b
$ Handle -> (e -> IO ()) -> IO (IO ())
mkReadStdError Handle
h e -> IO ()
trigger
Async ()
t <- IO (Async ()) -> m (Async ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Async ()) -> m (Async ())) -> IO (Async ()) -> m (Async ())
forall a b. (a -> b) -> a -> b
$ IO () -> IO (Async ())
forall a. IO a -> IO (Async a)
async IO ()
reader
(Event t e, Async ()) -> m (Event t e, Async ())
forall (m :: * -> *) a. Monad m => a -> m a
return (Event t e
e, Async ()
t)
(out :: Event t o
out, outThread :: Async ()
outThread) <- Handle -> m (Event t o, Async ())
output Handle
hOut
(err :: Event t e
err, errThread :: Async ()
errThread) <- Handle -> m (Event t e, Async ())
errOutput Handle
hErr
(ecOut :: Event t ExitCode
ecOut, ecTrigger :: ExitCode -> IO ()
ecTrigger) <- m (Event t ExitCode, ExitCode -> IO ())
forall t (m :: * -> *) a.
TriggerEvent t m =>
m (Event t a, a -> IO ())
newTriggerEvent
m (Async ()) -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m (Async ()) -> m ()) -> m (Async ()) -> m ()
forall a b. (a -> b) -> a -> b
$ IO (Async ()) -> m (Async ())
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Async ()) -> m (Async ())) -> IO (Async ()) -> m (Async ())
forall a b. (a -> b) -> a -> b
$ IO () -> IO (Async ())
forall a. IO a -> IO (Async a)
async (IO () -> IO (Async ())) -> IO () -> IO (Async ())
forall a b. (a -> b) -> a -> b
$ (IO () -> IO () -> IO ()) -> IO () -> IO () -> IO ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
finally ((Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> IO ()
P.cleanupProcess (Handle -> Maybe Handle
forall a. a -> Maybe a
Just Handle
hIn, Handle -> Maybe Handle
forall a. a -> Maybe a
Just Handle
hOut, Handle -> Maybe Handle
forall a. a -> Maybe a
Just Handle
hErr, ProcessHandle
ph)) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
ExitCode
waited <- ProcessHandle -> IO ExitCode
waitForProcess ProcessHandle
ph
((), ())
_ <- Async () -> Async () -> IO ((), ())
forall a b. Async a -> Async b -> IO (a, b)
waitBoth Async ()
outThread Async ()
errThread
ExitCode -> IO ()
ecTrigger ExitCode
waited
Process t o e -> m (Process t o e)
forall (m :: * -> *) a. Monad m => a -> m a
return (Process t o e -> m (Process t o e))
-> Process t o e -> m (Process t o e)
forall a b. (a -> b) -> a -> b
$ Process :: forall t o e.
ProcessHandle
-> Event t o
-> Event t e
-> Event t ExitCode
-> Event t Signal
-> Process t o e
Process
{ _process_exit :: Event t ExitCode
_process_exit = Event t ExitCode
ecOut
, _process_stdout :: Event t o
_process_stdout = Event t o
out
, _process_stderr :: Event t e
_process_stderr = Event t e
err
, _process_signal :: Event t Signal
_process_signal = (Maybe Signal -> Maybe Signal)
-> Event t (Maybe Signal) -> Event t Signal
forall (f :: * -> *) a b.
Filterable f =>
(a -> Maybe b) -> f a -> f b
fmapMaybe Maybe Signal -> Maybe Signal
forall a. a -> a
id Event t (Maybe Signal)
sigOut
, _process_handle :: ProcessHandle
_process_handle = ProcessHandle
ph
}
{-# DEPRECATED createRedirectedProcess "Use unsafeCreateProcessWithHandles instead." #-}
createRedirectedProcess
:: forall t m i o e. (MonadIO m, TriggerEvent t m, PerformEvent t m, MonadIO (Performable m))
=> (Handle -> IO (i -> IO ()))
-> (Handle -> (o -> IO ()) -> IO (IO ()))
-> (Handle -> (e -> IO ()) -> IO (IO ()))
-> P.CreateProcess
-> ProcessConfig t i
-> m (Process t o e)
createRedirectedProcess :: (Handle -> IO (i -> IO ()))
-> (Handle -> (o -> IO ()) -> IO (IO ()))
-> (Handle -> (e -> IO ()) -> IO (IO ()))
-> CreateProcess
-> ProcessConfig t i
-> m (Process t o e)
createRedirectedProcess = (Handle -> IO (i -> IO ()))
-> (Handle -> (o -> IO ()) -> IO (IO ()))
-> (Handle -> (e -> IO ()) -> IO (IO ()))
-> CreateProcess
-> ProcessConfig t i
-> m (Process t o e)
forall t (m :: * -> *) i o e.
(MonadIO m, TriggerEvent t m, PerformEvent t m,
MonadIO (Performable m)) =>
(Handle -> IO (i -> IO ()))
-> (Handle -> (o -> IO ()) -> IO (IO ()))
-> (Handle -> (e -> IO ()) -> IO (IO ()))
-> CreateProcess
-> ProcessConfig t i
-> m (Process t o e)
unsafeCreateProcessWithHandles