concurrent-output 1.9.2 → 1.10.0
raw patch · 5 files changed
+43/−94 lines, 5 filesdep ~processPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: process
API changes (from Hackage documentation)
- System.Console.Concurrent: data ConcurrentProcessHandle
- System.Console.Concurrent.Internal: ConcurrentProcessHandle :: ProcessHandle -> ConcurrentProcessHandle
- System.Console.Concurrent.Internal: [processWaiters] :: OutputHandle -> TMVar [Async ()]
- System.Console.Concurrent.Internal: [waitForProcessLock] :: OutputHandle -> TMVar ()
- System.Console.Concurrent.Internal: asyncProcessWaiter :: IO () -> IO ()
- System.Console.Concurrent.Internal: newtype ConcurrentProcessHandle
- System.Console.Concurrent.Internal: toConcurrentProcessHandle :: (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)
+ System.Console.Concurrent: type ConcurrentProcessHandle = ProcessHandle
+ System.Console.Concurrent.Internal: type ConcurrentProcessHandle = ProcessHandle
- System.Console.Concurrent: createProcessConcurrent :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)
+ System.Console.Concurrent: createProcessConcurrent :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
- System.Console.Concurrent: createProcessForeground :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)
+ System.Console.Concurrent: createProcessForeground :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
- System.Console.Concurrent.Internal: OutputHandle :: TMVar Lock -> TMVar OutputBuffer -> TMVar OutputBuffer -> TMVar Integer -> TMVar [Async ()] -> TMVar () -> OutputHandle
+ System.Console.Concurrent.Internal: OutputHandle :: TMVar Lock -> TMVar OutputBuffer -> TMVar OutputBuffer -> TMVar Integer -> OutputHandle
- System.Console.Concurrent.Internal: bgProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)
+ System.Console.Concurrent.Internal: bgProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
- System.Console.Concurrent.Internal: createProcessConcurrent :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)
+ System.Console.Concurrent.Internal: createProcessConcurrent :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
- System.Console.Concurrent.Internal: createProcessForeground :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)
+ System.Console.Concurrent.Internal: createProcessForeground :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
- System.Console.Concurrent.Internal: fgProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)
+ System.Console.Concurrent.Internal: fgProcess :: CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
Files
- CHANGELOG +12/−0
- System/Console/Concurrent.hs +2/−2
- System/Console/Concurrent/Internal.hs +25/−76
- System/Process/Concurrent.hs +2/−14
- concurrent-output.cabal +2/−2
CHANGELOG view
@@ -1,3 +1,15 @@+concurrent-output (1.10.0) unstable; urgency=medium++ * Simplified by removing workaround for waitForProcess race+ condition (https://github.com/haskell/process/issues/46).+ * Depends on process-1.6.0.0 which fixed that race.+ * ConcurrentProcessHandle is now a type alias for ProcessHandle,+ and waitForProcessConcurrent simply calls waitForProcess.+ These are now only provided to avoid breaking backwards+ compatability.++ -- Joey Hess <id@joeyh.name> Fri, 19 May 2017 11:54:49 -0400+ concurrent-output (1.9.2) unstable; urgency=medium * Allow process-1.6.0.0.
System/Console/Concurrent.hs view
@@ -20,12 +20,12 @@ Outputable(..), outputConcurrent, errorConcurrent,- ConcurrentProcessHandle, createProcessConcurrent,- waitForProcessConcurrent, createProcessForeground, flushConcurrentOutput, lockOutput,+ ConcurrentProcessHandle,+ waitForProcessConcurrent, -- * Low level access to the output buffer OutputBuffer, StdHandle(..),
System/Console/Concurrent/Internal.hs view
@@ -40,8 +40,6 @@ , outputBuffer :: TMVar OutputBuffer , errorBuffer :: TMVar OutputBuffer , outputThreads :: TMVar Integer- , processWaiters :: TMVar [Async ()]- , waitForProcessLock :: TMVar () } data Lock = Locked@@ -54,8 +52,6 @@ <*> newTMVarIO (OutputBuffer []) <*> newTMVarIO (OutputBuffer []) <*> newTMVarIO 0- <*> newTMVarIO []- <*> newEmptyTMVarIO -- | Holds a lock while performing an action. This allows the action to -- perform its own output to the console, without using functions from this@@ -186,69 +182,13 @@ h = toHandle stdh bv = bufferFor stdh -newtype ConcurrentProcessHandle = ConcurrentProcessHandle P.ProcessHandle--toConcurrentProcessHandle :: (Maybe Handle, Maybe Handle, Maybe Handle, P.ProcessHandle) -> (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)-toConcurrentProcessHandle (i, o, e, h) = (i, o, e, ConcurrentProcessHandle h)+-- | This alias is provided to avoid breaking backwards compatibility.+type ConcurrentProcessHandle = P.ProcessHandle --- | 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 `P.waitForProcess`+-- | Same as `P.waitForProcess`; provided to avoid breaking backwards+-- compatibility. waitForProcessConcurrent :: ConcurrentProcessHandle -> IO ExitCode-waitForProcessConcurrent (ConcurrentProcessHandle h) = - bracket lock unlock checkexit- where- lck = waitForProcessLock globalOutputHandle- lock = atomically $ tryPutTMVar lck ()- unlock True = atomically $ takeTMVar lck- unlock False = return ()- checkexit locked = maybe (waitsome locked) return- =<< P.getProcessExitCode h- waitsome True = do- let v = processWaiters globalOutputHandle- l <- atomically $ readTMVar v- if null l- -- Avoid waitAny [] which blocks forever- then P.waitForProcess h- else do- -- Wait for any of the running- -- processes to exit. It may or may not- -- be the one corresponding to the- -- ProcessHandle. If it is,- -- getProcessExitCode will succeed.- void $ tryIO $ waitAny l- checkexit True- waitsome False = do- -- Another thread took the lck first. Wait for that thread to- -- wait for one of the running processes to exit.- atomically $ do- putTMVar lck ()- takeTMVar lck- checkexit False---- Registers an action that waits for a process to exit,--- adding it to the processWaiters list, and removing it once the action--- completes.-asyncProcessWaiter :: IO () -> IO ()-asyncProcessWaiter waitaction = do- regdone <- newEmptyTMVarIO- waiter <- async $ do- self <- atomically (takeTMVar regdone)- waitaction `finally` unregister self- register waiter regdone- where- v = processWaiters globalOutputHandle- register waiter regdone = atomically $ do- l <- takeTMVar v- putTMVar v (waiter:l)- putTMVar regdone waiter- unregister waiter = atomically $ do- l <- takeTMVar v- putTMVar v (filter (/= waiter) l)+waitForProcessConcurrent = P.waitForProcess -- | Wrapper around `System.Process.createProcess` that prevents -- multiple processes that are running concurrently from writing@@ -266,7 +206,11 @@ -- 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 :: P.CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle) +--+-- Note that the the process is waited for by a background thread,+-- so unlike createProcess, neglecting to call waitForProcess will not+-- result in zombie processess.+createProcessConcurrent :: P.CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, P.ProcessHandle) createProcessConcurrent p | willOutput (P.std_out p) || willOutput (P.std_err p) = ifM tryTakeOutputLock@@ -275,31 +219,34 @@ ) | otherwise = do r@(_, _, _, h) <- P.createProcess p- asyncProcessWaiter $- void $ tryIO $ P.waitForProcess h- return (toConcurrentProcessHandle r)+ _ <- async $ void $ tryIO $ P.waitForProcess h+ return r -- | Wrapper around `System.Process.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 :: P.CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)+--+-- Note that the the process is waited for by a background thread,+-- so unlike createProcess, neglecting to call waitForProcess will not+-- result in zombie processess.+createProcessForeground :: P.CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, P.ProcessHandle) createProcessForeground p = do takeOutputLock fgProcess p -fgProcess :: P.CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)+fgProcess :: P.CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, P.ProcessHandle) fgProcess p = do r@(_, _, _, h) <- P.createProcess p `onException` dropOutputLock registerOutputThread -- Wait for the process to exit and drop the lock.- asyncProcessWaiter $ do+ _ <- async $ do void $ tryIO $ P.waitForProcess h unregisterOutputThread dropOutputLock- return (toConcurrentProcessHandle r)+ return r -bgProcess :: P.CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ConcurrentProcessHandle)+bgProcess :: P.CreateProcess -> IO (Maybe Handle, Maybe Handle, Maybe Handle, P.ProcessHandle) bgProcess p = do let p' = p { P.std_out = rediroutput (P.std_out p)@@ -314,11 +261,13 @@ , mungeret (P.std_err p) stderr_h , h )- asyncProcessWaiter $ void $ tryIO $ P.waitForProcess h+ -- Wait for the process for symmetry with fgProcess,+ -- which does the same.+ _ <- async $ void $ tryIO $ P.waitForProcess h outbuf <- setupOutputBuffer StdOut stdout_h errbuf <- setupOutputBuffer StdErr stderr_h void $ async $ bufferWriter [outbuf, errbuf]- return (toConcurrentProcessHandle r)+ return r where rediroutput ss | willOutput ss = P.CreatePipe
System/Process/Concurrent.hs view
@@ -9,26 +9,14 @@ module System.Process.Concurrent where import System.Console.Concurrent-import System.Console.Concurrent.Internal (ConcurrentProcessHandle(..)) import System.Process hiding (createProcess, waitForProcess) import System.IO import System.Exit -- | 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)-createProcess p = do- (i, o, e, ConcurrentProcessHandle h) <- createProcessConcurrent p- return (i, o, e, h)+createProcess = createProcessConcurrent -- | 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-waitForProcess = waitForProcessConcurrent . ConcurrentProcessHandle+waitForProcess = waitForProcessConcurrent
concurrent-output.cabal view
@@ -1,5 +1,5 @@ Name: concurrent-output-Version: 1.9.2+Version: 1.10.0 Cabal-Version: >= 1.8 License: BSD2 Maintainer: Joey Hess <id@joeyh.name>@@ -34,7 +34,7 @@ , text (>= 0.11.0 && < 1.3.0) , async (>= 2.0 && < 2.2) , stm (>= 2.0 && < 2.5)- , process (>= 1.1.0 && < 1.7.0)+ , process (>= 1.6.0 && < 1.7.0) , directory (>= 1.2.0 && < 1.4.0) , transformers (>= 0.3.0 && < 0.6.0) , exceptions (>= 0.6.0 && < 0.9.0)