-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Ungarble output from several threads -- -- Provides a simple interface for writing concurrent programs that need -- to output a lot of status messages to the console, or display multiple -- progress bars for different activities at the same time, or -- concurrently run external commands that output to the console. -- -- Built on top of that is a way of defining multiple output regions, -- which are automatically laid out on the screen and can be individually -- updated. Can be used for progress displays etc. -- @package concurrent-output @version 1.3.0 -- | Concurrent output handling, internals. -- -- May change at any time. module System.Console.Concurrent.Internal data OutputHandle OutputHandle :: TMVar Lock -> TMVar OutputBuffer -> TMVar OutputBuffer -> TMVar Integer -> TMVar [Async ()] -> TMVar () -> OutputHandle [outputLock] :: OutputHandle -> TMVar Lock [outputBuffer] :: OutputHandle -> TMVar OutputBuffer [errorBuffer] :: OutputHandle -> TMVar OutputBuffer [outputThreads] :: OutputHandle -> TMVar Integer [processWaiters] :: OutputHandle -> TMVar [Async ()] [waitForProcessLock] :: OutputHandle -> TMVar () data Lock Locked :: Lock -- | A shared global variable for the OutputHandle. globalOutputHandle :: OutputHandle -- | Holds a lock while performing an action. This allows the action to -- perform its own output to the console, without using functions from -- this module. -- -- While this is running, other threads that try to lockOutput will -- block. Any calls to outputConcurrent and -- createProcessConcurrent will not block, but the output will be -- buffered and displayed only once the action is done. lockOutput :: (MonadIO m, MonadMask m) => m a -> m a -- | Blocks until we have the output lock. takeOutputLock :: IO () -- | Tries to take the output lock, without blocking. tryTakeOutputLock :: IO Bool withLock :: (TMVar Lock -> STM a) -> IO a takeOutputLock' :: Bool -> IO Bool -- | Only safe to call after taking the output lock. dropOutputLock :: IO () -- | Use this around any actions that use outputConcurrent or -- createProcessConcurrent -- -- This is necessary to ensure that buffered concurrent output actually -- gets displayed before the program exits. withConcurrentOutput :: (MonadIO m, MonadMask m) => m a -> m a -- | Blocks until any processes started by createProcessConcurrent -- have finished, and any buffered output is displayed. -- -- withConcurrentOutput calls this at the end; you can call it -- anytime you want to flush output. flushConcurrentOutput :: IO () -- | Values that can be output. class Outputable v toOutput :: Outputable v => v -> Text -- | Displays a value to stdout. -- -- No newline is appended to the value, so if you want a newline, be sure -- to include it yourself. -- -- Uses locking to ensure that the whole output occurs atomically even -- when other threads are concurrently generating output. -- -- When something else is writing to the console at the same time, this -- does not block. It buffers the value, so it will be displayed once the -- other writer is done. outputConcurrent :: Outputable v => v -> IO () newtype ConcurrentProcessHandle ConcurrentProcessHandle :: ProcessHandle -> ConcurrentProcessHandle toConcurrentProcessHandle :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle) -- | Use this to wait for processes started with -- createProcessConcurrent and createProcessForeground, and -- get their exit status. -- -- Note that such processes are actually automatically waited for -- internally, so not calling this explicitly will not result in zombie -- processes. This behavior differs from waitForProcess waitForProcessConcurrent :: ConcurrentProcessHandle -> IO ExitCode asyncProcessWaiter :: IO () -> IO () -- | Wrapper around createProcess that prevents multiple processes -- that are running concurrently from writing to stdout/stderr at the -- same time. -- -- If the process does not output to stdout or stderr, it's run by -- createProcess entirely as usual. Only processes that can generate -- output are handled specially: -- -- A process is allowed to write to stdout and stderr in the usual way, -- assuming it can successfully take the output lock. -- -- When the output lock is held (ie, by another concurrent process, or -- because outputConcurrent is being called at the same time), the -- process is instead run with its stdout and stderr redirected to a -- buffer. The buffered output will be displayed as soon as the output -- lock becomes free. createProcessConcurrent :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle) -- | Wrapper around createProcess that makes sure a process is run -- in the foreground, with direct access to stdout and stderr. Useful -- when eg, running an interactive process. createProcessForeground :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle) fgProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle) bgProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle) willOutput :: StdStream -> Bool -- | Buffered output. data OutputBuffer OutputBuffer :: [OutputBufferedActivity] -> OutputBuffer data StdHandle StdOut :: StdHandle StdErr :: StdHandle toHandle :: StdHandle -> Handle bufferFor :: StdHandle -> TMVar OutputBuffer data OutputBufferedActivity Output :: Text -> OutputBufferedActivity InTempFile :: FilePath -> Bool -> OutputBufferedActivity [tempFile] :: OutputBufferedActivity -> FilePath [endsInNewLine] :: OutputBufferedActivity -> Bool data AtEnd AtEnd :: AtEnd data BufSig BufSig :: BufSig setupOutputBuffer :: StdHandle -> Handle -> StdStream -> Handle -> IO (StdHandle, MVar OutputBuffer, TMVar BufSig, TMVar AtEnd) outputDrainer :: StdStream -> Handle -> MVar OutputBuffer -> TMVar BufSig -> TMVar AtEnd -> IO () registerOutputThread :: IO () unregisterOutputThread :: IO () bufferWriter :: [(StdHandle, MVar OutputBuffer, TMVar BufSig, TMVar AtEnd)] -> IO () addOutputBuffer :: OutputBufferedActivity -> OutputBuffer -> IO OutputBuffer -- | Adds a value to the output buffer for later display. -- -- Note that buffering large quantities of data this way will keep it -- resident in memory until it can be displayed. While -- outputConcurrent uses temp files if the buffer gets too big, -- this STM function cannot do so. bufferOutputSTM :: Outputable v => StdHandle -> v -> STM () bufferOutputSTM' :: StdHandle -> OutputBuffer -> STM () -- | A STM action that waits for some buffered output to become available, -- and returns it. -- -- The function can select a subset of output when only some is desired; -- the fst part is returned and the snd is left in the buffer. -- -- This will prevent it from being displayed in the usual way, so you'll -- need to use emitOutputBuffer to display it yourself. outputBufferWaiterSTM :: (OutputBuffer -> (OutputBuffer, OutputBuffer)) -> STM [(StdHandle, OutputBuffer)] waitAnyBuffer :: OutputBuffer -> (OutputBuffer, OutputBuffer) -- | Use with outputBufferWaiterSTM to make it only return buffered -- output that ends with a newline. Anything buffered without a newline -- is left in the buffer. waitCompleteLines :: OutputBuffer -> (OutputBuffer, OutputBuffer) endsNewLine :: Text -> Bool -- | Emits the content of the OutputBuffer to the Handle -- -- If you use this, you should use lockOutput to ensure you're the -- only thread writing to the console. emitOutputBuffer :: StdHandle -> OutputBuffer -> IO () instance GHC.Classes.Eq System.Console.Concurrent.Internal.AtEnd instance GHC.Classes.Eq System.Console.Concurrent.Internal.OutputBuffer instance GHC.Classes.Eq System.Console.Concurrent.Internal.OutputBufferedActivity instance System.Console.Concurrent.Internal.Outputable Data.Text.Internal.Text instance System.Console.Concurrent.Internal.Outputable GHC.Base.String -- | Concurrent output handling. -- --
-- import Control.Concurrent.Async -- import System.Console.Concurrent -- -- main = withConcurrentOutput $ -- outputConcurrent "washed the car\n" -- `concurrently` -- outputConcurrent "walked the dog\n" -- `concurrently` -- createProcessConcurrent (proc "ls" []) --module System.Console.Concurrent -- | Use this around any actions that use outputConcurrent or -- createProcessConcurrent -- -- This is necessary to ensure that buffered concurrent output actually -- gets displayed before the program exits. withConcurrentOutput :: (MonadIO m, MonadMask m) => m a -> m a -- | Values that can be output. class Outputable v toOutput :: Outputable v => v -> Text -- | Displays a value to stdout. -- -- No newline is appended to the value, so if you want a newline, be sure -- to include it yourself. -- -- Uses locking to ensure that the whole output occurs atomically even -- when other threads are concurrently generating output. -- -- When something else is writing to the console at the same time, this -- does not block. It buffers the value, so it will be displayed once the -- other writer is done. outputConcurrent :: Outputable v => v -> IO () data ConcurrentProcessHandle -- | Wrapper around createProcess that prevents multiple processes -- that are running concurrently from writing to stdout/stderr at the -- same time. -- -- If the process does not output to stdout or stderr, it's run by -- createProcess entirely as usual. Only processes that can generate -- output are handled specially: -- -- A process is allowed to write to stdout and stderr in the usual way, -- assuming it can successfully take the output lock. -- -- When the output lock is held (ie, by another concurrent process, or -- because outputConcurrent is being called at the same time), the -- process is instead run with its stdout and stderr redirected to a -- buffer. The buffered output will be displayed as soon as the output -- lock becomes free. createProcessConcurrent :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle) -- | Use this to wait for processes started with -- createProcessConcurrent and createProcessForeground, and -- get their exit status. -- -- Note that such processes are actually automatically waited for -- internally, so not calling this explicitly will not result in zombie -- processes. This behavior differs from waitForProcess waitForProcessConcurrent :: ConcurrentProcessHandle -> IO ExitCode -- | Wrapper around createProcess that makes sure a process is run -- in the foreground, with direct access to stdout and stderr. Useful -- when eg, running an interactive process. createProcessForeground :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle) -- | Blocks until any processes started by createProcessConcurrent -- have finished, and any buffered output is displayed. -- -- withConcurrentOutput calls this at the end; you can call it -- anytime you want to flush output. flushConcurrentOutput :: IO () -- | Holds a lock while performing an action. This allows the action to -- perform its own output to the console, without using functions from -- this module. -- -- While this is running, other threads that try to lockOutput will -- block. Any calls to outputConcurrent and -- createProcessConcurrent will not block, but the output will be -- buffered and displayed only once the action is done. lockOutput :: (MonadIO m, MonadMask m) => m a -> m a -- | Buffered output. data OutputBuffer data StdHandle StdOut :: StdHandle StdErr :: StdHandle -- | Adds a value to the output buffer for later display. -- -- Note that buffering large quantities of data this way will keep it -- resident in memory until it can be displayed. While -- outputConcurrent uses temp files if the buffer gets too big, -- this STM function cannot do so. bufferOutputSTM :: Outputable v => StdHandle -> v -> STM () -- | A STM action that waits for some buffered output to become available, -- and returns it. -- -- The function can select a subset of output when only some is desired; -- the fst part is returned and the snd is left in the buffer. -- -- This will prevent it from being displayed in the usual way, so you'll -- need to use emitOutputBuffer to display it yourself. outputBufferWaiterSTM :: (OutputBuffer -> (OutputBuffer, OutputBuffer)) -> STM [(StdHandle, OutputBuffer)] waitAnyBuffer :: OutputBuffer -> (OutputBuffer, OutputBuffer) -- | Use with outputBufferWaiterSTM to make it only return buffered -- output that ends with a newline. Anything buffered without a newline -- is left in the buffer. waitCompleteLines :: OutputBuffer -> (OutputBuffer, OutputBuffer) -- | Emits the content of the OutputBuffer to the Handle -- -- If you use this, you should use lockOutput to ensure you're the -- only thread writing to the console. emitOutputBuffer :: StdHandle -> OutputBuffer -> IO () -- | Console regions are displayed near the bottom of the console, and can -- be updated concurrently by threads. Any other output displayed using -- outputConcurrent and createProcessConcurrent will scroll -- up above the open console regions. -- -- For example, this program: -- --
-- import Control.Concurrent.Async
-- import Control.Concurrent
-- import System.Console.Concurrent
-- import System.Console.Regions
-- import System.Process
--
-- main = displayConsoleRegions $ do
-- mapConcurrently download [1..5]
-- `concurrently` mapM_ message [1..10]
-- `concurrently` createProcessConcurrent (proc "echo" ["hello world"])
--
-- message :: Int -> IO ()
-- message n = do
-- threadDelay 500000
-- outputConcurrent ("Message " ++ show n ++ "\n")
--
-- download :: Int -> IO ()
-- download n = withConsoleRegion Linear $ \r -> do
-- setConsoleRegion r basemsg
-- go n r
-- where
-- basemsg = "Download " ++ show n
-- go c r
-- | c < 1 = finishConsoleRegion r (basemsg ++ " done!")
-- | otherwise = do
-- threadDelay 1000000
-- appendConsoleRegion r " ... "
-- go (c-1) r
--
--
-- Will display like this:
--
-- -- Message 1 -- hello world -- Message 2 -- Download 1 ... -- Download 2 ... -- Download 3 ... ---- -- Once the 1st download has finished, and another message has displayed, -- the console will update like this: -- --
-- Message 1 -- hello world -- Message 2 -- Download 1 done! -- Message 3 -- Download 2 ... ... -- Download 3 ... ... --module System.Console.Regions -- | Handles all display for the other functions in this module. -- -- Note that this uses lockOutput, so it takes over all output to -- the console while the passed IO action is running. As well as -- displaying the console regions, this handles display of anything -- buffered by outputConcurrent and -- createProcessConcurrent. -- -- When standard output is not an ANSI capable terminal, console regions -- are not displayed. displayConsoleRegions :: (MonadIO m, MonadMask m) => m a -> m a data ConsoleRegionHandle -- | Controls how a region is laid out in the console. -- -- Here's an annotated example of how the console layout works. -- --
-- scrolling...... -- scrolling...... -- scrolling...... -- aaaaaa......... -- Linear -- bbbbbbbbbbbbbbb -- Linear -- bbb............ (expanded to multiple lines) -- ccccccccc...... -- Linear -- ddddeeeefffffff -- [InLine] -- fffffggggg..... (expanded to multiple lines) --data RegionLayout Linear :: RegionLayout InLine :: ConsoleRegionHandle -> RegionLayout -- | Runs the action with a new console region, closing the region when the -- action finishes or on exception. withConsoleRegion :: (MonadIO m, MonadMask m) => RegionLayout -> (ConsoleRegionHandle -> m a) -> m a -- | Opens a new console region for output. openConsoleRegion :: RegionLayout -> IO ConsoleRegionHandle -- | Closes a console region. Once closed, the region is removed from the -- display. closeConsoleRegion :: ConsoleRegionHandle -> IO () -- | Values that can be displayed in a region. class Displayable v toRegionContent :: Displayable v => v -> STM RegionContent -- | Sets the value to display within a console region. -- -- It's fine for the value to be longer than the terminal is wide, or to -- include newlines ('\n'). Regions expand to multiple lines as -- necessary. -- -- The value can include ANSI SGR escape sequences for changing the -- colors etc of all or part of a region. -- -- Other ANSI escape sequences, especially those doing cursor movement, -- will mess up the layouts of regions. Caveat emptor. setConsoleRegion :: Displayable v => ConsoleRegionHandle -> v -> IO () -- | Appends the value to whatever was already on display within a console -- region. appendConsoleRegion :: Outputable v => ConsoleRegionHandle -> v -> IO () -- | Closes the console region and displays the passed value in the -- scrolling area above the active console regions. finishConsoleRegion :: Outputable v => ConsoleRegionHandle -> v -> IO () -- | STM version of openConsoleRegion. Allows atomically opening -- multiple regions at the same time, which guarantees they are adjacent. -- --
-- [r1, r2, r3] <- atomically $ -- replicateM 3 (openConsoleRegionSTM Linear T.empty) --openConsoleRegionSTM :: Displayable v => RegionLayout -> v -> STM ConsoleRegionHandle -- | Makes a new region, but does not add it to the display. newConsoleRegionSTM :: Displayable v => RegionLayout -> v -> STM ConsoleRegionHandle closeConsoleRegionSTM :: ConsoleRegionHandle -> STM () -- | STM version of setConsoleRegion setConsoleRegionSTM :: Displayable v => ConsoleRegionHandle -> v -> STM () -- | STM version of appendConsoleRegion appendConsoleRegionSTM :: Outputable v => ConsoleRegionHandle -> v -> STM () data RegionContent RegionContent :: (TVar Text) -> RegionContent RegionContentSTM :: (STM Text) -> RegionContent -- | Reads the content of a region. readRegionContent :: ConsoleRegionHandle -> STM Text -- | On unix systems, this TVar is automatically updated when the terminal -- is resized. consoleSize :: TVar (Window Int) type Width = Int consoleWidth :: STM Width -- | All the regions that are currently displayed on the screen. -- -- The list is ordered from the bottom of the screen up. Reordering it -- will change the order in which regions are displayed. It's also fine -- to remove, duplicate, or add new regions to the list. regionList :: TMVar [ConsoleRegionHandle] instance GHC.Show.Show System.Console.Regions.LineUpdate instance GHC.Classes.Eq System.Console.Regions.LineUpdate instance GHC.Classes.Eq System.Console.Regions.RegionLayout instance GHC.Classes.Eq System.Console.Regions.ConsoleRegionHandle instance System.Console.Regions.Displayable GHC.Base.String instance System.Console.Regions.Displayable Data.Text.Internal.Text instance System.Console.Regions.Displayable (GHC.Conc.Sync.STM Data.Text.Internal.Text) -- | The functions exported by this module are intended to be drop-in -- replacements for those from System.Process, when converting a whole -- program to use System.Console.Concurrent. module System.Process.Concurrent -- | Calls createProcessConcurrent -- -- You should use the waitForProcess in this module on the resulting -- ProcessHandle. Using System.Process.waitForProcess instead can have -- mildly unexpected results. createProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -- | Calls waitForProcessConcurrent -- -- You should only use this on a ProcessHandle obtained by calling -- createProcess from this module. Using this with a ProcessHandle -- obtained from System.Process.createProcess etc will have extremely -- unexpected results; it can wait a very long time before returning. waitForProcess :: ProcessHandle -> IO ExitCode