@@ -130,6 +130,7 @@ module System.Process.Typed
130130 ) where
131131
132132import Control.Exception hiding (bracket , finally )
133+ import Control.Monad ((>=>) , guard )
133134import Control.Monad.IO.Class
134135import qualified System.Process as P
135136import System.IO (hClose )
@@ -256,32 +257,32 @@ startProcess pConfig'@ProcessConfig {..} = liftIO $ do
256257 -- Process didn't exit yet, let's terminate it and
257258 -- then call waitForProcess ourselves
258259 Left _ -> do
259- eres <- try $ P. terminateProcess pHandle
260- ec <-
261- case eres of
262- Left e
263- -- On Windows, with the single-threaded runtime, it
264- -- seems that if a process has already exited, the
265- -- call to terminateProcess will fail with a
266- -- permission denied error. To work around this, we
267- -- catch this exception and then immediately
268- -- waitForProcess. There's a chance that there may be
269- -- other reasons for this permission error to appear,
270- -- in which case this code may allow us to wait too
271- -- long for a child process instead of erroring out.
272- -- Recommendation: always use the multi-threaded
273- -- runtime!
274- | isPermissionError e && not multiThreadedRuntime && isWindows ->
275- P. waitForProcess pHandle
276- | otherwise -> throwIO e
277- Right () -> P. waitForProcess pHandle
260+ terminateProcess pHandle
261+ ec <- P. waitForProcess pHandle
278262 success <- atomically $ tryPutTMVar pExitCode ec
279263 evaluate $ assert success ()
280264
281265 return Process {.. }
282266 where
283267 pConfig = clearStreams pConfig'
284268
269+ terminateProcess :: P. ProcessHandle -> IO ()
270+ terminateProcess p = do
271+ -- On Windows, with the single-threaded runtime, it seems that if a
272+ -- process has already exited, the call to terminateProcess will fail
273+ -- with a permission denied error. To work around this, we ignore this
274+ -- exception. There's a chance that there may be other reasons for this
275+ -- permission error to appear, in which case this code may allow us to
276+ -- wait too long for a child process instead of erroring out on a
277+ -- subsequent call to `waitForProcess`.
278+ -- Recommendation: always use the multi-threaded runtime!
279+ ignorePermissionErrorOnSingleThreadedWindows $ P. terminateProcess p
280+
281+ ignorePermissionErrorOnSingleThreadedWindows :: IO () -> IO ()
282+ ignorePermissionErrorOnSingleThreadedWindows = tryJust (guard . p) >=> either return return
283+ where
284+ p e = isPermissionError e && not multiThreadedRuntime && isWindows
285+
285286foreign import ccall unsafe " rtsSupportsBoundThreads"
286287 multiThreadedRuntime :: Bool
287288
0 commit comments