-- 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.1.0 -- | 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 -> ByteString -- | 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 () -- | 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, or after the command has finished. createProcessConcurrent :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -- | This must be used to wait for processes started with -- createProcessConcurrent. -- -- This is necessary because waitForProcess has a race condition -- when two threads check the same process. If the race is triggered, one -- thread will successfully wait, but the other throws a DoesNotExist -- exception. waitForProcessConcurrent :: ProcessHandle -> IO ExitCode -- | 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 () instance GHC.Classes.Eq System.Console.Concurrent.AtEnd instance GHC.Classes.Eq System.Console.Concurrent.OutputBuffer instance GHC.Classes.Eq System.Console.Concurrent.OutputBufferedActivity instance System.Console.Concurrent.Outputable Data.ByteString.Internal.ByteString instance System.Console.Concurrent.Outputable Data.Text.Internal.Text instance System.Console.Concurrent.Outputable GHC.Base.String -- | 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
--
-- main = displayConsoleRegions $ do
-- mapConcurrently download [1..5] `concurrently` mapM_ message [1..10]
--
-- 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 -- 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 -- 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 -- ddd eee fffffff -- [InLine] -- ffff ggggg..... (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 () -- | Sets the value to display within a console region. setConsoleRegion :: Outputable 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 on -- adjacent lines. -- --
-- [r1, r2, r3] <- atomically $ -- replicateM 3 (openConsoleRegionSTM Linear) --openConsoleRegionSTM :: RegionLayout -> STM ConsoleRegionHandle closeConsoleRegionSTM :: ConsoleRegionHandle -> STM () setConsoleRegionSTM :: Outputable v => ConsoleRegionHandle -> v -> STM () appendConsoleRegionSTM :: Outputable v => ConsoleRegionHandle -> v -> STM () -- | Updates the list of regions. 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. updateRegionListSTM :: ([ConsoleRegionHandle] -> [ConsoleRegionHandle]) -> STM () instance GHC.Classes.Eq System.Console.Regions.RegionLayout instance GHC.Classes.Eq System.Console.Regions.ConsoleRegionHandle instance GHC.Classes.Eq System.Console.Regions.Region