Skip to content

Commit 7426298

Browse files
committed
Fix cleanup when stdin/stdout handles are inherited and are Nothing
1 parent 94bb378 commit 7426298

File tree

1 file changed

+52
-15
lines changed

1 file changed

+52
-15
lines changed

src/Streamly/Internal/System/Process.hs

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ module Streamly.Internal.System.Process
7777
, inheritStdin
7878
, inheritStdout
7979
, pipeStdErr
80+
, pipeStdin
81+
, pipeStdout
8082

8183
-- * Exceptions
8284
, ProcessFailure (..)
@@ -226,6 +228,12 @@ mkConfig _ _ = Config False
226228
pipeStdErr :: Config -> Config
227229
pipeStdErr (Config _) = Config True
228230

231+
pipeStdin :: Config -> Config
232+
pipeStdin (Config _) = Config True
233+
234+
pipeStdout :: Config -> Config
235+
pipeStdout (Config _) = Config True
236+
229237
inheritStdin :: Config -> Config
230238
inheritStdin (Config _) = Config True
231239

@@ -465,6 +473,12 @@ waitForChildTree = waitForDescendants
465473
pipeStdErr :: Config -> Config
466474
pipeStdErr (Config cfg) = Config $ cfg { std_err = CreatePipe }
467475

476+
pipeStdin :: Config -> Config
477+
pipeStdin (Config cleanupCfg cfg) = Config cleanupCfg $ cfg { std_in = CreatePipe }
478+
479+
pipeStdout :: Config -> Config
480+
pipeStdout (Config cleanupCfg cfg) = Config cleanupCfg $ cfg { std_out = CreatePipe }
481+
468482
inheritStdin :: Config -> Config
469483
inheritStdin (Config cfg) = Config $ cfg { std_in = Inherit }
470484

@@ -530,7 +544,7 @@ cleanupNormal (_, _, _, procHandle) = do
530544
-- still hanging around.
531545
cleanupException ::
532546
(Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> IO ()
533-
cleanupException (Just stdinH, Just stdoutH, stderrMaybe, ph) = do
547+
cleanupException (stdinMaybe, stdoutMaybe, stderrMaybe, ph) = do
534548
-- Send a SIGTERM to the process
535549
#ifdef USE_NATIVE
536550
terminate ph
@@ -541,8 +555,8 @@ cleanupException (Just stdinH, Just stdoutH, stderrMaybe, ph) = do
541555
-- Ideally we should be closing the handle without flushing the buffers so
542556
-- that we cannot get a SIGPIPE. But there seems to be no way to do that as
543557
-- of now so we just ignore the SIGPIPE.
544-
hClose stdinH `catch` eatSIGPIPE
545-
hClose stdoutH
558+
whenJust (\stdinH -> hClose stdinH `catch` eatSIGPIPE) stdinMaybe
559+
whenJust hClose stdoutMaybe
546560
whenJust hClose stderrMaybe
547561

548562
-- Non-blocking wait for the process to go away
@@ -565,7 +579,6 @@ cleanupException (Just stdinH, Just stdoutH, stderrMaybe, ph) = do
565579
_ -> False
566580

567581
eatSIGPIPE e = unless (isSIGPIPE e) $ throwIO e
568-
cleanupException _ = error "cleanupProcess: Not reachable"
569582

570583
-- | Creates a system process from an executable path and arguments. For the
571584
-- default attributes used to create the process see 'mkConfig'.
@@ -630,6 +643,11 @@ pipeChunksWithAction run modCfg path args =
630643

631644
alloc = createProc' modCfg path args
632645

646+
-- Note: It is allowed to inheritStdout or inheritStderr but not both as that
647+
-- would not generate a stream for further processing.
648+
-- inheritStdin has no affect, we always pipe the input stream to the process's
649+
-- stdin
650+
633651
-- | Like 'pipeChunksEither' but use the specified configuration to run the
634652
-- process.
635653
{-# INLINE pipeChunksEitherWith #-}
@@ -641,10 +659,12 @@ pipeChunksEitherWith ::
641659
-> Stream m (Array Word8) -- ^ Input stream
642660
-> Stream m (Either (Array Word8) (Array Word8)) -- ^ Output stream
643661
pipeChunksEitherWith modifier path args input =
644-
pipeChunksWithAction run (modifier . pipeStdErr) path args
662+
pipeChunksWithAction run (pipeStdin . modifier . pipeStdErr) path args
645663

646664
where
647665

666+
run (_, Nothing, Nothing, _) =
667+
error "pipeChunksEitherWith: only one of stdout or stderr can be inherited"
648668
run (Just stdinH, Just stdoutH, Just stderrH, _) =
649669
putChunksClose stdinH input
650670
`parallel` fmap Left (toChunksClose stderrH)
@@ -704,6 +724,10 @@ pipeBytesEither path args input =
704724
rightRdr = fmap Right Array.reader
705725
in UNFOLD_EACH (Unfold.either leftRdr rightRdr) output
706726

727+
-- Note: inheritStdin, inheritStdout have no affect, we always pipe
728+
-- the input stream to the process's stdin and pipe the stdout to the
729+
-- resulting stream
730+
707731
-- | Like 'pipeChunks' but use the specified configuration to run the process.
708732
{-# INLINE pipeChunksWith #-}
709733
pipeChunksWith ::
@@ -714,13 +738,14 @@ pipeChunksWith ::
714738
-> Stream m (Array Word8) -- ^ Input stream
715739
-> Stream m (Array Word8) -- ^ Output stream
716740
pipeChunksWith modifier path args input =
717-
pipeChunksWithAction run modifier path args
741+
pipeChunksWithAction run (pipeStdout . pipeStdin . modifier) path args
718742

719743
where
720744

721745
run (Just stdinH, Just stdoutH, _, _) =
722-
putChunksClose stdinH input `parallel` toChunksClose stdoutH
723-
run _ = error "pipeChunksWith: Not reachable"
746+
putChunksClose stdinH input
747+
`parallel` toChunksClose stdoutH
748+
run _ = error "pipeChunksWith: unreachable"
724749

725750
-- | @pipeChunks file args input@ runs the executable @file@ specified by
726751
-- its name or path using @args@ as arguments and @input@ stream as its
@@ -853,6 +878,10 @@ pipeChars path args input =
853878
-- Generation
854879
-------------------------------------------------------------------------------
855880

881+
-- Note: It is allowed to inheritStdout or inheritStderr but not both as that
882+
-- would not generate a stream for further processing and would result in unintuitive
883+
-- behaviour
884+
856885
-- | Like 'toChunksEither' but use the specified configuration to run the
857886
-- process.
858887
{-# INLINE toChunksEitherWith #-}
@@ -863,14 +892,18 @@ toChunksEitherWith ::
863892
-> [String] -- ^ Arguments
864893
-> Stream m (Either (Array Word8) (Array Word8)) -- ^ Output stream
865894
toChunksEitherWith modifier path args =
866-
pipeChunksWithAction run (modifier . inheritStdin . pipeStdErr) path args
895+
pipeChunksWithAction run (modifier . inheritStdin . pipeStdErr . pipeStdout) path args
867896

868897
where
869898

870-
run (_, Just stdoutH, Just stderrH, _) =
871-
fmap Left (toChunksClose stderrH)
872-
`parallel` fmap Right (toChunksClose stdoutH)
873-
run _ = error "toChunksEitherWith: Not reachable"
899+
run (_, Nothing, Nothing, _) =
900+
error "toChunksEitherWith: only one of stdout or stderr can be inherited"
901+
run (_, stdoutMaybe, stderrMaybe, _) =
902+
fmap Left (whenJustS toChunksClose stderrMaybe)
903+
`parallel` fmap Right (whenJustS toChunksClose stdoutMaybe)
904+
905+
-- Note: inheritStdout has no affect, we always pipe stdout to the resulting
906+
-- stream
874907

875908
-- | Like 'toChunks' but use the specified configuration to run the process.
876909
{-# INLINE toChunksWith #-}
@@ -881,12 +914,12 @@ toChunksWith ::
881914
-> [String] -- ^ Arguments
882915
-> Stream m (Array Word8) -- ^ Output stream
883916
toChunksWith modifier path args =
884-
pipeChunksWithAction run (modifier . inheritStdin) path args
917+
pipeChunksWithAction run (pipeStdout . modifier . inheritStdin) path args
885918

886919
where
887920

888921
run (_, Just stdoutH, _, _) = toChunksClose stdoutH
889-
run _ = error "toChunksWith: Not reachable"
922+
run _ = error "toChunksWith: Not reachable"
890923

891924
-- | @toBytesEither path args@ runs the executable at @path@ using @args@ as
892925
-- arguments and returns the output of the process as a stream of 'Either'
@@ -1193,3 +1226,7 @@ daemon modCfg path args =
11931226
(setSession NewSession . modCfg)
11941227
path args
11951228
in fmap (either undefined id) r
1229+
1230+
1231+
whenJustS :: Applicative m => (a -> Stream m b) -> Maybe a -> Stream m b
1232+
whenJustS action mb = maybe Stream.nil action mb

0 commit comments

Comments
 (0)