Skip to content

Commit bc3a7f6

Browse files
committed
Ensure that waitForProcess is never called more than once (fixes #69)
1 parent bbf07a9 commit bc3a7f6

File tree

4 files changed

+27
-18
lines changed

4 files changed

+27
-18
lines changed

ChangeLog.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# ChangeLog for typed-process
22

3+
## 0.2.12.0
4+
5+
* Ensure that `waitForProcess` is never called more than once
6+
[#70](https://github.com/fpco/typed-process/pull/70)
7+
38
## 0.2.11.0
49

510
* Expose more from `System.Process.Typed.Internal`

package.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: typed-process
2-
version: 0.2.11.0
2+
version: 0.2.12.0
33
synopsis: Run external processes, with strong typing of streams
44
description: Please see the tutorial at <https://github.com/fpco/typed-process#readme>
55
category: System

src/System/Process/Typed.hs

+11-17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE LambdaCase #-}
23
{-# LANGUAGE TypeFamilies #-}
34
{-# LANGUAGE DeriveDataTypeable #-}
45
{-# LANGUAGE RecordWildCards #-}
@@ -130,13 +131,15 @@ module System.Process.Typed
130131
) where
131132

132133
import Control.Exception hiding (bracket, finally)
134+
import Control.Monad ((>=>), guard)
133135
import Control.Monad.IO.Class
134136
import qualified System.Process as P
135137
import System.IO (hClose)
136138
import System.IO.Error (isPermissionError)
137139
import Control.Concurrent (threadDelay)
138-
import Control.Concurrent.Async (asyncWithUnmask, cancel, waitCatch)
139-
import Control.Concurrent.STM (newEmptyTMVarIO, atomically, putTMVar, TMVar, readTMVar, tryReadTMVar, STM, tryPutTMVar, throwSTM, catchSTM)
140+
import Control.Concurrent.Async (asyncWithUnmask)
141+
import qualified Control.Concurrent.Async as Async
142+
import Control.Concurrent.STM (newEmptyTMVarIO, atomically, putTMVar, TMVar, readTMVar, tryReadTMVar, STM, throwSTM, catchSTM)
140143
import System.Exit (ExitCode (ExitSuccess, ExitFailure))
141144
import System.Process.Typed.Internal
142145
import qualified Data.ByteString.Lazy as L
@@ -239,27 +242,18 @@ startProcess pConfig'@ProcessConfig {..} = liftIO $ do
239242
atomically $ putTMVar pExitCode ec
240243
return ec
241244

245+
let waitForProcess = Async.wait waitingThread :: IO ExitCode
242246
let pCleanup = pCleanup1 `finally` do
243-
-- First: stop calling waitForProcess, so that we can
244-
-- avoid race conditions where the process is removed from
245-
-- the system process table while we're trying to
246-
-- terminate it.
247-
cancel waitingThread
248-
249-
-- Now check if the process had already exited
250-
eec <- waitCatch waitingThread
251-
252-
case eec of
247+
_ :: ExitCode <- Async.poll waitingThread >>= \ case
253248
-- Process already exited, nothing to do
254-
Right _ec -> return ()
249+
Just r -> either throwIO return r
255250

256251
-- Process didn't exit yet, let's terminate it and
257252
-- then call waitForProcess ourselves
258-
Left _ -> do
253+
Nothing -> do
259254
terminateProcess pHandle
260-
ec <- P.waitForProcess pHandle
261-
success <- atomically $ tryPutTMVar pExitCode ec
262-
evaluate $ assert success ()
255+
waitForProcess
256+
return ()
263257

264258
return Process {..}
265259
where

test/System/Process/TypedSpec.hs

+10
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module System.Process.TypedSpec (spec) where
55
import System.Process.Typed
66
import System.Process.Typed.Internal
77
import System.IO
8+
import Control.Exception
89
import Control.Concurrent.Async (Concurrently (..))
910
import Control.Concurrent.STM (atomically)
1011
import Test.Hspec
@@ -170,3 +171,12 @@ spec = do
170171
it "empty param are showed" $
171172
let expected = "Raw command: podman exec --detach-keys \"\" ctx bash\n"
172173
in show (proc "podman" ["exec", "--detach-keys", "", "ctx", "bash"]) `shouldBe` expected
174+
175+
describe "stopProcess" $ do
176+
it "never calls waitForProcess more than once (fix for #69)" $ do
177+
-- https://github.com/fpco/typed-process/issues/70
178+
let config = setStdout createPipe (proc "echo" ["foo"])
179+
withProcessWait config $ \p -> do
180+
_ <- S.hGetContents (getStdout p)
181+
throwIO DivideByZero
182+
`shouldThrow` (== DivideByZero)

0 commit comments

Comments
 (0)