From 357ffb9fd34ebd36e07dece8e45450dbd2f0e8ec Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Wed, 28 Oct 2015 00:12:38 -0400 Subject: concurrency docs --- debian/changelog | 2 +- src/Propellor/Message.hs | 16 ++++++++-------- src/Propellor/Property/Concurrent.hs | 37 +++++++++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/debian/changelog b/debian/changelog index 1699b27b..6c154e1a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -18,7 +18,7 @@ propellor (2.13.0) UNRELEASED; urgency=medium * combineWith now takes an additional parameter to control how revert actions are combined (API change). * Added Propellor.Property.Concurrent for concurrent properties. - (Note that no command output multiplexing is currently done.) + * execProcess and everything built on it is now concurrent output safe. * Add File.isCopyOf. Thanks, Per Olofsson. -- Joey Hess Sat, 24 Oct 2015 15:16:45 -0400 diff --git a/src/Propellor/Message.hs b/src/Propellor/Message.hs index 4be8263e..3792129b 100644 --- a/src/Propellor/Message.hs +++ b/src/Propellor/Message.hs @@ -238,22 +238,22 @@ messagesDone = lockOutput $ do setTitle "propellor: done" hFlush stdout --- | Wrapper around `System.Process.createProcess` that prevents processes --- that are running concurrently from writing to the stdout/stderr at the --- same time. +-- | Wrapper around `System.Process.createProcess` that prevents +-- multiple processes that are running concurrently from writing +-- to stdout/stderr at the same time. -- --- The first process run by createProcess is allowed to write to +-- The first process is allowed to write to -- stdout and stderr in the usual way. -- --- However, if a second createProcess runs concurrently with the +-- However, if another process runs concurrently with the -- first, any stdout or stderr that would have been displayed by it is -- instead buffered. The buffered output will be displayed the next time it -- is safe to do so (ie, after the first process exits). -- --- `Propellor.Property.Cmd` has some other useful actions for running --- commands, which are based on this. --- -- Also does debug logging of all commands run. +-- +-- Unless you manually import System.Process, every part of propellor +-- that runs a process uses this. createProcessConcurrent :: P.CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, P.ProcessHandle) createProcessConcurrent p | hasoutput (P.std_out p) || hasoutput (P.std_err p) = diff --git a/src/Propellor/Property/Concurrent.hs b/src/Propellor/Property/Concurrent.hs index 645a5dfd..74afecc4 100644 --- a/src/Propellor/Property/Concurrent.hs +++ b/src/Propellor/Property/Concurrent.hs @@ -1,14 +1,38 @@ {-# LANGUAGE FlexibleContexts #-} --- | Note that any output of commands run by --- concurrent properties will be scrambled together. +-- | Propellor properties can be made to run concurrently, using this +-- module. This can speed up propellor, at the expense of using more CPUs +-- and other resources. +-- +-- It's up to you to make sure that properties that you make run concurrently +-- don't implicitly depend on one-another. The worst that can happen +-- though, is that propellor fails to ensure some of the properties, +-- and tells you what went wrong. +-- +-- Another potential problem is that output of concurrent properties could +-- interleave into a scrambled mess. This is mostly prevented; all messages +-- output by propellor are concurrency safe, including `errorMessage`, +-- `infoMessage`, etc. However, if you write a property that directly +-- uses `print` or `putStrLn`, you can still experience this problem. +-- +-- Similarly, when properties run external commands, the command's output +-- can be a problem for concurrency. No need to worry; +-- `Propellor.Property.Cmd.createProcess` is concurrent output safe +-- (it actually uses `Propellor.Message.createProcessConcurrent`), and +-- everything else in propellor that runs external commands is built on top +-- of that. Of course, if you import System.Process and use it in a +-- property, you can bypass that and shoot yourself in the foot. +-- +-- Finally, anything that directly accesses the tty can bypass +-- these protections. That's sometimes done for eg, password prompts. +-- A well-written property should avoid running interactive commands +-- anyway. module Propellor.Property.Concurrent ( concurrently, concurrentList, props, getNumProcessors, - withCapabilities, concurrentSatisfy, ) where @@ -20,6 +44,12 @@ import GHC.Conc (getNumProcessors) import Control.Monad.RWS.Strict -- | Ensures two properties concurrently. +-- +-- > & foo `concurrently` bar +-- +-- To ensure three properties concurrently, just use this combinator twice: +-- +-- > & foo `concurrently` bar `concurrently` baz concurrently :: (IsProp p1, IsProp p2, Combines p1 p2, IsProp (CombinedType p1 p2)) => p1 @@ -95,6 +125,7 @@ withCapabilities n a = bracket setup cleanup (const a) return c cleanup = liftIO . setNumCapabilities +-- | Running Propellor actions concurrently. concurrentSatisfy :: Propellor Result -> Propellor Result -> Propellor Result concurrentSatisfy a1 a2 = do h <- ask -- cgit v1.2.3