Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
This contains the innards of Cliff. You shouldn't need anything that's in this module; instead, use Pipes.Cliff.
Exit code and waiting for processes: as of base 4.7, there was a
bug in waitForProcess
which may arise if you have
multiple threads waiting for a single process to finish. Thus this
module is set up so that only one thread does the wait, and it
places the result in an MVar. See
- data CmdSpec
- convertCmdSpec :: CmdSpec -> CmdSpec
- data Activity
- data Outbound
- data HandleDesc
- data Oopsie = Oopsie Activity HandleDesc CmdSpec IOException
- renderOopsie :: String -> Oopsie -> String
- defaultHandler :: Oopsie -> IO ()
- data NonPipe
- convertNonPipe :: Maybe NonPipe -> StdStream
- data CreateProcess = CreateProcess {}
- squelch :: Monad m => a -> m ()
- procSpec :: String -> [String] -> CreateProcess
- convertCreateProcess :: Maybe NonPipe -> Maybe NonPipe -> Maybe NonPipe -> CreateProcess -> CreateProcess
- type Lock = MVar ()
- newLock :: IO Lock
- withLock :: Lock -> IO a -> IO a
- type Var a = MVar a
- newVar :: a -> IO (Var a)
- modifyVar :: Var a -> (a -> IO (a, b)) -> IO b
- modifyVar_ :: Var a -> (a -> IO a) -> IO ()
- readVar :: Var a -> IO a
- type Barrier a = MVar a
- newBarrier :: IO (Barrier a)
- signalBarrier :: Barrier a -> a -> IO ()
- waitBarrier :: Barrier a -> IO a
- once :: IO a -> IO (IO a)
- messageBox :: IO (a -> STM Bool, STM (Maybe a), STM ())
- sendBox :: TVar Bool -> TMVar a -> a -> STM Bool
- recvBox :: TVar Bool -> TMVar a -> STM (Maybe a)
- sealer :: TVar Bool -> STM ()
- produceFromBox :: MonadIO m => STM (Maybe a) -> Producer a m ()
- sendToBox :: MonadIO m => (a -> STM Bool) -> Consumer a m ()
- data Console = Console {}
- isStillRunning :: ProcessHandle -> IO Bool
- data ProcessHandle = ProcessHandle {}
- originalCreateProcess :: ProcessHandle -> CreateProcess
- addReleaser :: ProcessHandle -> IO () -> IO ()
- terminateProcess :: ProcessHandle -> IO ()
- waitForProcess :: ProcessHandle -> IO ExitCode
- newProcessHandle :: Maybe NonPipe -> Maybe NonPipe -> Maybe NonPipe -> CreateProcess -> IO ProcessHandle
- handleException :: Activity -> HandleDesc -> CmdSpec -> (Oopsie -> IO ()) -> IOException -> IO ()
- closeHandleNoThrow :: Handle -> HandleDesc -> CmdSpec -> (Oopsie -> IO ()) -> IO ()
- conveyor :: Effect (SafeT IO) a -> IO (Async a)
- safeEffect :: Effect (SafeT IO) a -> IO a
- newMailbox :: (MonadSafe mi, MonadSafe mo) => IO (Consumer a mi (), Producer a mo (), STM ())
- withProcess :: IO (a, ProcessHandle) -> (a -> IO b) -> IO b
- withConveyor :: Effect (SafeT IO) a -> IO b -> IO b
- bufSize :: Int
- initHandle :: (MonadSafe mi, MonadCatch (Base mi)) => HandleDesc -> (Console -> Handle) -> ProcessHandle -> (Handle -> mi a) -> mi a
- consumeToHandle :: (MonadSafe mi, MonadCatch (Base mi)) => ProcessHandle -> Consumer ByteString mi ()
- produceFromHandle :: (MonadSafe mi, MonadCatch (Base mi)) => Outbound -> ProcessHandle -> Producer ByteString mi ()
- finishProxy :: Async () -> ProcessHandle -> IO ExitCode
- runInputHandle :: (MonadSafe mi, MonadCatch (Base mi)) => ProcessHandle -> Consumer ByteString mi ExitCode
- runOutputHandle :: (MonadSafe mi, MonadCatch (Base mi)) => Outbound -> ProcessHandle -> Producer ByteString mi ExitCode
- pipeInput :: (MonadSafe mi, MonadCatch (Base mi)) => NonPipe -> NonPipe -> CreateProcess -> IO (Consumer ByteString mi ExitCode, ProcessHandle)
- pipeOutput :: (MonadSafe mo, MonadCatch (Base mo)) => NonPipe -> NonPipe -> CreateProcess -> IO (Producer ByteString mo ExitCode, ProcessHandle)
- pipeError :: (MonadSafe me, MonadCatch (Base me)) => NonPipe -> NonPipe -> CreateProcess -> IO (Producer ByteString me ExitCode, ProcessHandle)
- pipeInputOutput :: (MonadSafe mi, MonadCatch (Base mi), MonadSafe mo, MonadCatch (Base mo)) => NonPipe -> CreateProcess -> IO ((Consumer ByteString mi ExitCode, Producer ByteString mo ExitCode), ProcessHandle)
- pipeInputError :: (MonadSafe mi, MonadCatch (Base mi), MonadSafe me, MonadCatch (Base me)) => NonPipe -> CreateProcess -> IO ((Consumer ByteString mi ExitCode, Producer ByteString me ExitCode), ProcessHandle)
- pipeOutputError :: (MonadSafe mo, MonadCatch (Base mo), MonadSafe me, MonadCatch (Base me)) => NonPipe -> CreateProcess -> IO ((Producer ByteString mo ExitCode, Producer ByteString me ExitCode), ProcessHandle)
- pipeInputOutputError :: (MonadSafe mi, MonadCatch (Base mi), MonadSafe mo, MonadCatch (Base mo), MonadSafe me, MonadCatch (Base me)) => CreateProcess -> IO ((Consumer ByteString mi ExitCode, Producer ByteString mo ExitCode, Producer ByteString me ExitCode), ProcessHandle)
Data types
Like CmdSpec
in System.Process, but also has an
instance for Show
.
convertCmdSpec :: CmdSpec -> CmdSpec Source
Errors
When dealing with a Handle
, errors can occur when reading from,
writing to, or closing the handle.
The two kinds of outbound handles.
data HandleDesc Source
Describes a handle. From the perspective of the subprocess; for
example, Input
means that this handle is connected to the
process's standard input.
Describes all IO exceptions. The Oopsie
contains the
IOException
itself, along with the CmdSpec
that was running
when the exception occurred.
The exceptions that are caught and placed into an Oopsie
may
arise from reading data from or writing data to a Handle
. In
these errors, the associated Producer
or Consumer
will
terminate (which may trigger various cleanup actions in the
MonadSafe
computation) but the exception itself is not
re-thrown; rather, it is passed to the handler
. Similarly, an
exception may occur while closing a handle; these exceptions are
caught, not rethrown, and are passed to the handler
. If an
exception arises when terminating a process (I'm not sure this is
possible) then it is also caught, not rethrown, and passed to the
handler
.
If an exception arises when creating a process--such as a command
not being found--the exception is not caught, handled, or
passed to the handler
. In addition, no exceptions are caught
if they originated during a waitForProcess
. (I can't
conceive of how any synchronous exceptions could arise from
waitForProcess
, but if they do, Cliff does not handle
them.) Also, an Oopsie
is created only for an IOException
;
no other exceptions of any kind are caught or handled. However,
exceptions of any kind will still trigger appropriate cleanup
actions in the MonadSafe
computation.
Formats an Oopsie
for display.
defaultHandler :: Oopsie -> IO () Source
The default handler when receiving an Oopsie
; simply uses
renderOopsie
to format it nicely and put it on standard error.
Side effects: gets the program name from the environment, and prints the Oopsie to standard error.
Configuration types
data CreateProcess Source
Like CreateProcess
in System.Process,
this gives the necessary information to create a subprocess. All
but one of these fields is also present in
CreateProcess
, and they all have the same meaning;
the only field that is different is the handler
field.
CreateProcess | |
|
squelch :: Monad m => a -> m () Source
Do not show or do anything with exceptions; useful to use as a
handler
.
Side effects: None.
:: String | The name of the program to run, such as |
-> [String] | Command-line arguments |
-> CreateProcess |
Create a CreateProcess
record with default settings. The
default settings are:
- a raw command (as opposed to a shell command) is created
- the current working directory is not changed from the parent process
- the environment is not changed from the parent process
- the parent's file descriptors (other than standard input, standard output, and standard error) are inherited
- no new process group is created
delegate_ctlc
isFalse
handler
isdefaultHandler
convertCreateProcess :: Maybe NonPipe -> Maybe NonPipe -> Maybe NonPipe -> CreateProcess -> CreateProcess Source
MVar types
Lock
Guarantees single-thread access
All MVar idioms thanks to Neil Mitchell: http://neilmitchell.blogspot.com/2012/06/flavours-of-mvar_04.html
Var
modifyVar_ :: Var a -> (a -> IO a) -> IO () Source
Barrier
type Barrier a = MVar a Source
Starts with no value, is written to once, and is read one or more times.
newBarrier :: IO (Barrier a) Source
signalBarrier :: Barrier a -> a -> IO () Source
waitBarrier :: Barrier a -> IO a Source
MVar abstractions
Mailboxes
messageBox :: IO (a -> STM Bool, STM (Maybe a), STM ()) Source
Creates a new mailbox. Returns an action to send to the mailbox; this action will return False if the mailbox is sealed, or True if the message was successfully placed in the mailbox. Also returns an action to retrieve from the mailbox, which returns Nothing if the mailbox is sealed, or Just if there is a value to be retrieved. Also returns an action to seal the mailbox.
Console
Data that is computed once, after the process has been created. After computation, this data does not change.
Console | |
|
isStillRunning :: ProcessHandle -> IO Bool Source
Is this process still running?
Side effects: examines the process handle to see if it has yet returned a value. Does not block; should return immediately.
data ProcessHandle Source
Allows you to terminate the process, as well as to obtain some information about the process.
originalCreateProcess :: ProcessHandle -> CreateProcess Source
Tells you the CreateProcess
that was originally used to create
the process associated with this ProcessHandle
.
addReleaser :: ProcessHandle -> IO () -> IO () Source
Add a finalizer to the ProcessHandle. When the finalizers are run, all exceptions are ignored, except asynchronous exceptions, which are masked.
terminateProcess :: ProcessHandle -> IO () Source
Terminates a process. Sends the process a SIGTERM
, which does
not absolutely guarantee that it will exit. Closes any Handle
s
that were created for the process through Cliff, and terminates any
associated background threads that were moving data to and from the
process. Use this function this with bracket
to ensure proper cleanup of resources.
waitForProcess :: ProcessHandle -> IO ExitCode Source
Gets the exit code of the process that belongs to the ProcessHandle
.
Often you can get the exit code through more idiomatic pipes
functions, as the various Proxy
return the ExitCode
. Sometimes
though it can be difficult to use the pipes
idioms to get the
exit code, so this function is here.
Side effects: may block if process has not yet exited.
newProcessHandle :: Maybe NonPipe -> Maybe NonPipe -> Maybe NonPipe -> CreateProcess -> IO ProcessHandle Source
Creates a new ProcessHandle.
Side effects: Does not create the process right away; instead, creates an IO action that, when run, will create the process. This IO action contains another IO action that, when run, will return the process exit code.
In addition, the IO action will fork a simple thread that will
immediately wait for the process. In effect, this means there is
immediately a thread that will wait for the process to exit.
Because this IO action was created with once
, that means only
one thread ever does the wait
, which avoids a bug in
System.Process.
Exception handling
handleException :: Activity -> HandleDesc -> CmdSpec -> (Oopsie -> IO ()) -> IOException -> IO () Source
closeHandleNoThrow :: Handle -> HandleDesc -> CmdSpec -> (Oopsie -> IO ()) -> IO () Source
Close a handle. Catches any exceptions and passes them to the handler.
Threads
conveyor :: Effect (SafeT IO) a -> IO (Async a) Source
Runs in the background an effect, typically one that is moving data from one process to another. For examples of its usage, see Pipes.Cliff.Examples.
Effects
safeEffect :: Effect (SafeT IO) a -> IO a Source
Runs in the foreground an effect in the SafeT
monad.
Mailboxes
newMailbox :: (MonadSafe mi, MonadSafe mo) => IO (Consumer a mi (), Producer a mo (), STM ()) Source
Creates a new mailbox and returns Proxy
that stream values
into and out of the mailbox. Each Proxy
is equipped with a
finalizer that will seal the mailbox immediately after production
or consumption has completed, even if such completion is not due
to an exhausted mailbox. This will signal to the other side of
the mailbox that the mailbox is sealed.
Also returns an STM action to seal the box manually.
Exception safety
:: IO (a, ProcessHandle) | Creates the process |
-> (a -> IO b) | Uses the process |
-> IO b |
Creates a process, uses it, and terminates it when the last
computation ends. Don't try to use any of the process resources
after the last computation ends, because the process will already
have been terminated. For an example of its use, see
standardOutputAndErrorBracketed
.
:: Effect (SafeT IO) a | The |
-> IO b | The rest of the computation to run |
-> IO b |
Runs an Effect
in the backgroud (typically one that is moving
data from one process to another). If the background thread is
still running when the second computation ends, the background
thread is terminated. For an example of its use, see
standardOutputAndErrorBracketed
.
Production from and consumption to Handle
s
I have no idea what this should be. I'll start with a simple small value and see how it works.
:: (MonadSafe mi, MonadCatch (Base mi)) | |
=> HandleDesc | Used for error messages |
-> (Console -> Handle) | Fetch the handle to close from the |
-> ProcessHandle | Has the |
-> (Handle -> mi a) | The remainder of the computation. |
-> mi a |
Initialize a handle. Returns a computation in the MonadSafe
monad. That computation has a registered finalizer that will close
a particular handle that is found in the ProcessHandle
. As a side
effect, the IO action creating the ProcessHandle
is viewed, meaning that
the process will launch if it hasn't already done so.
consumeToHandle :: (MonadSafe mi, MonadCatch (Base mi)) => ProcessHandle -> Consumer ByteString mi () Source
produceFromHandle :: (MonadSafe mi, MonadCatch (Base mi)) => Outbound -> ProcessHandle -> Producer ByteString mi () Source
Produce values from a process standard output. Process is started if it isn't already.
finishProxy :: Async () -> ProcessHandle -> IO ExitCode Source
Given an Async
, waits for that thread to finish processing
values. When it completes, wait for the process exit code.
:: (MonadSafe mi, MonadCatch (Base mi)) | |
=> ProcessHandle | |
-> Consumer ByteString mi ExitCode |
Takes all steps necessary to get a Consumer
for standard
input:
- Creates a
Consumer
that will consume to the process standard input. ThisConsumer
registers a MonadSafe releaser that will close the handle. - Creates a mailbox, with a
Producer
from the mailbox and aConsumer
to the mailbox. Each of theseProxy
has a MonadSafe releaser that will close the mailbox. - Spwans a thread to run an
Effect
that connects theConsumer
that is connected to the handle to theProducer
from the mailbox. In a typical UNIX pipeline situation (where the process keeps its stdin open as long as it is getting input) thisEffect
will stop running only when the mailbox is sealed. - Registers a releaser in the Panel (not in the MonadSafe computation) to destroy the thread; this is in case the user terminates the process.
- Returns a
Consumer
. TheConsumer
consumes to the mailbox. ThisConsumer
returns the exit code of this process (but remember that the ultimate result of theProxy
depends on which component terminates first).
Does not register in the MonadSafe
an action to cancel the
background thread. Data might still be moving to the process even
if the Proxy
has shut down. Let the thread terminate through
mailbox closure or a broken pipe.
:: (MonadSafe mi, MonadCatch (Base mi)) | |
=> Outbound | |
-> ProcessHandle | |
-> Producer ByteString mi ExitCode |
Takes all steps necessary to get a Producer
for standard
input. Sets up a mailbox, runs a conveyor in the background. Then
receives streaming data, and then gets the process exit code.
Creating Proxy
:: (MonadSafe mi, MonadCatch (Base mi)) | |
=> NonPipe | Standard output |
-> NonPipe | Standard error |
-> CreateProcess | |
-> IO (Consumer ByteString mi ExitCode, ProcessHandle) | A |
Create a Consumer
for standard input.
:: (MonadSafe mo, MonadCatch (Base mo)) | |
=> NonPipe | Standard input |
-> NonPipe | Standard error |
-> CreateProcess | |
-> IO (Producer ByteString mo ExitCode, ProcessHandle) | A |
Create a Producer
for standard output.
:: (MonadSafe me, MonadCatch (Base me)) | |
=> NonPipe | Standard input |
-> NonPipe | Standard output |
-> CreateProcess | |
-> IO (Producer ByteString me ExitCode, ProcessHandle) | A |
Create a Producer
for standard error.
:: (MonadSafe mi, MonadCatch (Base mi), MonadSafe mo, MonadCatch (Base mo)) | |
=> NonPipe | Standard error |
-> CreateProcess | |
-> IO ((Consumer ByteString mi ExitCode, Producer ByteString mo ExitCode), ProcessHandle) | A |
:: (MonadSafe mi, MonadCatch (Base mi), MonadSafe me, MonadCatch (Base me)) | |
=> NonPipe | Standard output |
-> CreateProcess | |
-> IO ((Consumer ByteString mi ExitCode, Producer ByteString me ExitCode), ProcessHandle) | A |
:: (MonadSafe mo, MonadCatch (Base mo), MonadSafe me, MonadCatch (Base me)) | |
=> NonPipe | Standard input |
-> CreateProcess | |
-> IO ((Producer ByteString mo ExitCode, Producer ByteString me ExitCode), ProcessHandle) | A |
:: (MonadSafe mi, MonadCatch (Base mi), MonadSafe mo, MonadCatch (Base mo), MonadSafe me, MonadCatch (Base me)) | |
=> CreateProcess | |
-> IO ((Consumer ByteString mi ExitCode, Producer ByteString mo ExitCode, Producer ByteString me ExitCode), ProcessHandle) | A |