@@ -23,6 +23,8 @@ module Streamly.Streams.Parallel
2323 , Parallel
2424 , parallely
2525 , parallel
26+ , parallelEndByFirst
27+ , parallelEndByAny
2628
2729 -- * Function application
2830 , mkParallel
@@ -42,10 +44,13 @@ import Control.Monad.Reader.Class (MonadReader(..))
4244import Control.Monad.State.Class (MonadState(..))
4345import Control.Monad.Trans.Class (MonadTrans(lift))
4446import Data.Functor (void)
47+import Data.IORef (readIORef, writeIORef)
4548import Data.Maybe (fromJust)
4649import Data.Semigroup (Semigroup(..))
4750import Prelude hiding (map)
4851
52+import qualified Data.Set as Set
53+
4954import Streamly.Streams.SVar (fromSVar)
5055import Streamly.Streams.Serial (map)
5156import Streamly.SVar
@@ -88,22 +93,28 @@ runOne st m winfo = foldStreamShared st yieldk single stop m
8893 >> withLimitCheck (void $ liftIO $ mrun $ runOne st r winfo)
8994
9095{-# NOINLINE forkSVarPar #-}
91−forkSVarPar :: (IsStream t, MonadAsync m) => t m a -> t m a -> t m a
92−forkSVarPar m r = mkStream $ \st yld sng stp -> do
93− sv <- newParallelVar st
96+forkSVarPar :: (IsStream t, MonadAsync m)
97+ => SVarStopStyle -> t m a -> t m a -> t m a
98+forkSVarPar ss m r = mkStream $ \st yld sng stp -> do
99+ sv <- newParallelVar ss st
94100 pushWorkerPar sv (runOne st{streamVar = Just sv} $ toStream m)
101+ case ss of
102+ StopBy -> liftIO $ do
103+ set <- readIORef (workerThreads sv)
104+ writeIORef (svarStopBy sv) $ Set.elemAt 0 set
105+ _ -> return ()
95106 pushWorkerPar sv (runOne st{streamVar = Just sv} $ toStream r)
96107 foldStream st yld sng stp (fromSVar sv)
97108
98109{-# INLINE joinStreamVarPar #-}
99110joinStreamVarPar :: (IsStream t, MonadAsync m)
100− => SVarStyle -> t m a -> t m a -> t m a
101−joinStreamVarPar style m1 m2 = mkStream $ \st yld sng stp ->
111+ => SVarStyle -> SVarStopStyle -> t m a -> t m a -> t m a
112+joinStreamVarPar style ss m1 m2 = mkStream $ \st yld sng stp ->
102113 case streamVar st of
103− Just sv | svarStyle sv == style -> do
114+ Just sv | svarStyle sv == style && svarStopStyle sv == ss -> do
104115 pushWorkerPar sv (runOne st $ toStream m1)
105116 foldStreamShared st yld sng stp m2
106− _ -> foldStreamShared st yld sng stp (forkSVarPar m1 m2)
117+ _ -> foldStreamShared st yld sng stp (forkSVarPar ss m1 m2)
107118
108119-- | XXX we can implement it more efficienty by directly implementing instead
109120-- of combining streams using parallel.
@@ -118,7 +129,28 @@ consMParallel m r = fromStream $ K.yieldM m `parallel` (toStream r)
118129-- @since 0.2.0
119130{-# INLINE parallel #-}
120131parallel :: (IsStream t, MonadAsync m) => t m a -> t m a -> t m a
121−parallel = joinStreamVarPar ParallelVar
132+parallel = joinStreamVarPar ParallelVar StopNone
133+
134+-- This is a co-parallel like combinator for streams, where first stream is the
135+-- main stream and the rest are just supporting it, when the first ends
136+-- everything ends.
137+--
138+-- | Like `parallel` but stops the output as soon as the first stream stops.
139+--
140+-- @since 0.7.0
141+{-# INLINE parallelEndByFirst #-}
142+parallelEndByFirst :: (IsStream t, MonadAsync m) => t m a -> t m a -> t m a
143+parallelEndByFirst = joinStreamVarPar ParallelVar StopBy
144+
145+-- This is a race like combinator for streams.
146+--
147+-- | Like `parallel` but stops the output as soon as any of the two streams
148+-- stops.
149+--
150+-- @since 0.7.0
151+{-# INLINE parallelEndByAny #-}
152+parallelEndByAny :: (IsStream t, MonadAsync m) => t m a -> t m a -> t m a
153+parallelEndByAny = joinStreamVarPar ParallelVar StopAny
122154
123155------------------------------------------------------------------------------
124156-- Convert a stream to parallel
@@ -126,7 +158,7 @@ parallel = joinStreamVarPar ParallelVar
126158
127159mkParallel :: (IsStream t, MonadAsync m) => t m a -> m (t m a)
128160mkParallel m = do
129− sv <- newParallelVar defState
161+ sv <- newParallelVar StopNone defState
130162 pushWorkerPar sv (runOne defState{streamVar = Just sv} $ toStream m)
131163 return $ fromSVar sv
132164
@@ -137,7 +169,7 @@ mkParallel m = do
137169{-# INLINE applyParallel #-}
138170applyParallel :: (IsStream t, MonadAsync m) => (t m a -> t m b) -> t m a -> t m b
139171applyParallel f m = mkStream $ \st yld sng stp -> do
140− sv <- newParallelVar (adaptState st)
172+ sv <- newParallelVar StopNone (adaptState st)
141173 pushWorkerPar sv (runOne st{streamVar = Just sv} (toStream m))
142174 foldStream st yld sng stp $ f $ fromSVar sv
143175
@@ -148,7 +180,7 @@ applyParallel f m = mkStream $ \st yld sng stp -> do
148180{-# INLINE foldParallel #-}
149181foldParallel :: (IsStream t, MonadAsync m) => (t m a -> m b) -> t m a -> m b
150182foldParallel f m = do
151− sv <- newParallelVar defState
183+ sv <- newParallelVar StopNone defState
152184 pushWorkerPar sv (runOne defState{streamVar = Just sv} $ toStream m)
153185 f $ fromSVar sv
154186