@@ -95,6 +95,7 @@ module Streamly.Streams.StreamD
95 , fromListM
95 , fromListM
96 , fromStreamK
96 , fromStreamK
97 , fromStreamD
97 , fromStreamD
99 -- * Elimination
100 -- * Elimination
100 -- ** General Folds
101 -- ** General Folds
@@ -136,6 +137,7 @@ module Streamly.Streams.StreamD
140 -- ** Flattening nested streams
142 -- ** Flattening nested streams
141 , concatMapM
143 , concatMapM
@@ -260,6 +262,8 @@ module Streamly.Streams.StreamD
260 , indexedR
262 , indexedR
261 , zipWith
263 , zipWith
262 , zipWithM
264 , zipWithM
264 -- * Comparisons
268 -- * Comparisons
@@ -292,19 +296,25 @@ module Streamly.Streams.StreamD
292 , resumeDecodeUtf8Either
296 , resumeDecodeUtf8Either
293 , decodeUtf8Arrays
297 , decodeUtf8Arrays
294 , decodeUtf8ArraysLenient
298 , decodeUtf8ArraysLenient
300+ -- * Concurrent Application
298−import Control.Exception (Exception, SomeException)
307+import Control.Exception (Exception, SomeException, fromException)
299−import Control.Monad (void)
308+import Control.Monad (void, when)
300−import Control.Monad.Catch (MonadCatch)
309+import Control.Monad.Catch (MonadCatch, throwM)
301import Control.Monad.IO.Class (MonadIO(..))
310import Control.Monad.IO.Class (MonadIO(..))
302import Control.Monad.Reader (ReaderT)
311import Control.Monad.Reader (ReaderT)
303import Control.Monad.State.Strict (StateT)
312import Control.Monad.State.Strict (StateT)
304import Control.Monad.Trans (MonadTrans(lift))
313import Control.Monad.Trans (MonadTrans(lift))
305import Data.Bits (shiftR, shiftL, (.|.), (.&.))
314import Data.Bits (shiftR, shiftL, (.|.), (.&.))
306import Data.Functor.Identity (Identity(..))
315import Data.Functor.Identity (Identity(..))
316+import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
307−import Data.Maybe (fromJust, isJust)
317+import Data.Maybe (fromJust, isJust, isNothing)
308import Data.Word (Word32)
318import Data.Word (Word32)
309import Foreign.Ptr (Ptr)
319import Foreign.Ptr (Ptr)
310import Foreign.Storable (Storable(..))
320import Foreign.Storable (Storable(..))
@@ -315,6 +325,7 @@ import GHC.Ptr (Ptr (..))
315import GHC.Types (SPEC(..))
325import GHC.Types (SPEC(..))
316import GHC.Word (Word8(..))
326import GHC.Word (Word8(..))
317import System.IO.Unsafe (unsafePerformIO)
327import System.IO.Unsafe (unsafePerformIO)
328+import System.Mem (performMajorGC)
318import Prelude
329import Prelude
319 hiding (map, mapM, mapM_, repeat, foldr, last, take, filter,
330 hiding (map, mapM, mapM_, repeat, foldr, last, take, filter,
320 takeWhile, drop, dropWhile, all, any, maximum, minimum, elem,
331 takeWhile, drop, dropWhile, all, any, maximum, minimum, elem,
@@ -325,18 +336,22 @@ import Prelude
325import qualified Control.Monad.Catch as MC
336import qualified Control.Monad.Catch as MC
326import qualified Control.Monad.Reader as Reader
337import qualified Control.Monad.Reader as Reader
327import qualified Control.Monad.State.Strict as State
338import qualified Control.Monad.State.Strict as State
339+import qualified Prelude
341+import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
329import Streamly.Internal.Memory.Array.Types (Array(..))
342import Streamly.Internal.Memory.Array.Types (Array(..))
330import Streamly.Internal.Data.Fold.Types (Fold(..))
343import Streamly.Internal.Data.Fold.Types (Fold(..))
331import Streamly.Internal.Data.Pipe.Types (Pipe(..), PipeState(..))
344import Streamly.Internal.Data.Pipe.Types (Pipe(..), PipeState(..))
332−import Streamly.Internal.Data.SVar (MonadAsync, defState, adaptState)
345+import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
333import Streamly.Internal.Data.Unfold.Types (Unfold(..))
346import Streamly.Internal.Data.Unfold.Types (Unfold(..))
334import Streamly.Internal.Data.Strict (Tuple'(..))
347import Streamly.Internal.Data.Strict (Tuple'(..))
336import Streamly.Internal.Data.Stream.StreamD.Type
349import Streamly.Internal.Data.Stream.StreamD.Type
350+import Streamly.Internal.Data.SVar
338import qualified Streamly.Internal.Data.Pipe.Types as Pipe
352import qualified Streamly.Internal.Data.Pipe.Types as Pipe
339import qualified Streamly.Internal.Memory.Array.Types as A
353import qualified Streamly.Internal.Memory.Array.Types as A
354+import qualified Streamly.Internal.Data.Fold as FL
340import qualified Streamly.Memory.Ring as RB
355import qualified Streamly.Memory.Ring as RB
341import qualified Streamly.Streams.StreamK as K
356import qualified Streamly.Streams.StreamK as K
@@ -648,10 +663,100 @@ fromListM = Stream step
648toStreamD :: (K.IsStream t, Monad m) => t m a -> Stream m a
663toStreamD :: (K.IsStream t, Monad m) => t m a -> Stream m a
649toStreamD = fromStreamK . K.toStream
664toStreamD = fromStreamK . K.toStream
666+-------------------------------------------------------------------------------
667+-- Generation from SVar
668+-------------------------------------------------------------------------------
670+data FromSVarState t m a =
672+ | FromSVarRead (SVar t m a)
673+ | FromSVarLoop (SVar t m a) [ChildEvent a]
674+ | FromSVarDone (SVar t m a)
676+{-# INLINE_NORMAL fromSVar #-}
677+fromSVar :: (MonadAsync m) => SVar t m a -> Stream m a
678+fromSVar svar = Stream step FromSVarInit
681+ {-# INLINE_LATE step #-}
682+ step _ FromSVarInit = do
683+ ref <- liftIO $ newIORef ()
684+ _ <- liftIO $ mkWeakIORef ref hook
685+ -- when this copy of svar gets garbage collected "ref" will get
686+ -- garbage collected and our GC hook will be called.
687+ let sv = svar{svarRef = Just ref}
688+ return $ Skip (FromSVarRead sv)
692+ {-# NOINLINE hook #-}
694+ when (svarInspectMode svar) $ do
695+ r <- liftIO $ readIORef (svarStopTime (svarStats svar))
696+ when (isNothing r) $
697+ printSVar svar "SVar Garbage Collected"
699+ -- If there are any SVars referenced by this SVar a GC will prompt
700+ -- them to be cleaned up quickly.
701+ when (svarInspectMode svar) performMajorGC
703+ step _ (FromSVarRead sv) = do
704+ list <- readOutputQ sv
705+ -- Reversing the output is important to guarantee that we process the
706+ -- outputs in the same order as they were generated by the constituent
708+ return $ Skip $ FromSVarLoop sv (Prelude.reverse list)
710+ step _ (FromSVarLoop sv []) = do
711+ done <- postProcess sv
712+ return $ Skip $ if done
713+ then (FromSVarDone sv)
714+ else (FromSVarRead sv)
716+ step _ (FromSVarLoop sv (ev : es)) = do
718+ ChildYield a -> return $ Yield a (FromSVarLoop sv es)
719+ ChildStop tid e -> do
720+ accountThread sv tid
723+ stop <- shouldStop tid
726+ liftIO (cleanupSVar sv)
727+ return $ Skip (FromSVarDone sv)
728+ else return $ Skip (FromSVarLoop sv es)
730+ case fromException ex of
732+ return $ Skip (FromSVarLoop sv es)
733+ Nothing -> liftIO (cleanupSVar sv) >> throwM ex
737+ case svarStopStyle sv of
738+ StopNone -> return False
739+ StopAny -> return True
741+ sid <- liftIO $ readIORef (svarStopBy sv)
742+ return $ if tid == sid then True else False
744+ step _ (FromSVarDone sv) = do
745+ when (svarInspectMode sv) $ do
746+ t <- liftIO $ getTime Monotonic
747+ liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
748+ liftIO $ printSVar sv "SVar Done"
751+-------------------------------------------------------------------------------
752+-- Hoisting the inner monad
753+-------------------------------------------------------------------------------
651{-# INLINE_NORMAL hoist #-}
755{-# INLINE_NORMAL hoist #-}
652hoist :: Monad n => (forall x. m x -> n x) -> Stream m a -> Stream n a
756hoist :: Monad n => (forall x. m x -> n x) -> Stream m a -> Stream n a
653hoist f (Stream step state) = (Stream step' state)
757hoist f (Stream step state) = (Stream step' state)
759+ {-# INLINE_LATE step' #-}
655 step' gst st = do
760 step' gst st = do
656 r <- f $ step (adaptState gst) st
761 r <- f $ step (adaptState gst) st
657 return $ case r of
762 return $ case r of
@@ -659,7 +764,7 @@ hoist f (Stream step state) = (Stream step' state)
659 Skip s -> Skip s
764 Skip s -> Skip s
660 Stop -> Stop
765 Stop -> Stop
662−{-# INLINE_NORMAL generally #-}
767+{-# INLINE generally #-}
663generally :: Monad m => Stream Identity a -> Stream m a
768generally :: Monad m => Stream Identity a -> Stream m a
664generally = hoist (return . runIdentity)
769generally = hoist (return . runIdentity)
@@ -668,6 +773,7 @@ liftInner :: (Monad m, MonadTrans t, Monad (t m))
668 => Stream m a -> Stream (t m) a
773 => Stream m a -> Stream (t m) a
669liftInner (Stream step state) = Stream step' state
774liftInner (Stream step state) = Stream step' state
776+ {-# INLINE_LATE step' #-}
671 step' gst st = do
777 step' gst st = do
672 r <- lift $ step (adaptState gst) st
778 r <- lift $ step (adaptState gst) st
673 return $ case r of
779 return $ case r of
@@ -679,6 +785,7 @@ liftInner (Stream step state) = Stream step' state
679runReaderT :: Monad m => s -> Stream (ReaderT s m) a -> Stream m a
785runReaderT :: Monad m => s -> Stream (ReaderT s m) a -> Stream m a
680runReaderT sval (Stream step state) = Stream step' state
786runReaderT sval (Stream step state) = Stream step' state
788+ {-# INLINE_LATE step' #-}
682 step' gst st = do
789 step' gst st = do
683 r <- Reader.runReaderT (step (adaptState gst) st) sval
790 r <- Reader.runReaderT (step (adaptState gst) st) sval
684 return $ case r of
791 return $ case r of
@@ -690,6 +797,7 @@ runReaderT sval (Stream step state) = Stream step' state
690evalStateT :: Monad m => s -> Stream (StateT s m) a -> Stream m a
797evalStateT :: Monad m => s -> Stream (StateT s m) a -> Stream m a
691evalStateT sval (Stream step state) = Stream step' (state, sval)
798evalStateT sval (Stream step state) = Stream step' (state, sval)
800+ {-# INLINE_LATE step' #-}
693 step' gst (st, sv) = do
801 step' gst (st, sv) = do
694 (r, sv') <- State.runStateT (step (adaptState gst) st) sv
802 (r, sv') <- State.runStateT (step (adaptState gst) st) sv
695 return $ case r of
803 return $ case r of
@@ -701,6 +809,7 @@ evalStateT sval (Stream step state) = Stream step' (state, sval)
701runStateT :: Monad m => s -> Stream (StateT s m) a -> Stream m (s, a)
809runStateT :: Monad m => s -> Stream (StateT s m) a -> Stream m (s, a)
702runStateT sval (Stream step state) = Stream step' (state, sval)
810runStateT sval (Stream step state) = Stream step' (state, sval)
812+ {-# INLINE_LATE step' #-}
704 step' gst (st, sv) = do
813 step' gst (st, sv) = do
705 (r, sv') <- State.runStateT (step (adaptState gst) st) sv
814 (r, sv') <- State.runStateT (step (adaptState gst) st) sv
706 return $ case r of
815 return $ case r of
@@ -3426,6 +3535,37 @@ zipWithM f (Stream stepa ta) (Stream stepb tb) = Stream step (ta, tb, Nothing)
3426zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
3535zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
3427zipWith f = zipWithM (\a b -> return (f a b))
3536zipWith f = zipWithM (\a b -> return (f a b))
3538+-- | Like 'zipWithM' but zips concurrently i.e. both the streams being zipped
3539+-- are generated concurrently.
3541+{-# INLINE_NORMAL zipAsyncWithM #-}
3542+zipAsyncWithM :: MonadAsync m
3543+ => (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
3544+zipAsyncWithM f m1 m2 = Stream step Nothing
3548+ {-# INLINE_LATE step #-}
3549+ step gst Nothing = do
3550+ ma <- mkParallel (adaptState gst) m1
3551+ mb <- mkParallel (adaptState gst) m2
3552+ return $ Skip (Just $ zipWithM f ma mb)
3554+ step gst (Just (UnStream step1 st)) = do
3557+ Yield a s -> Yield a (Just $ Stream step1 s)
3558+ Skip s -> Skip (Just $ Stream step1 s)
3561+-- | Like 'zipWith' but zips concurrently i.e. both the streams being zipped
3562+-- are generated concurrently.
3564+{-# INLINE zipAsyncWith #-}
3565+zipAsyncWith :: MonadAsync m
3566+ => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
3567+zipAsyncWith f = zipAsyncWithM (\a b -> return (f a b))
3429------------------------------------------------------------------------------
3569------------------------------------------------------------------------------
3430-- Merging
3570-- Merging
3431------------------------------------------------------------------------------
3571------------------------------------------------------------------------------
@@ -3983,3 +4123,106 @@ encodeUtf8 (Stream step state) = Stream step' (EncodeState state WNil)
3983 Skip s -> Skip (EncodeState s WNil)
4123 Skip s -> Skip (EncodeState s WNil)
3984 Stop -> Stop
4124 Stop -> Stop
3985 step' _ (EncodeState s (WCons x xs)) = return $ Yield x (EncodeState s xs)
4125 step' _ (EncodeState s (WCons x xs)) = return $ Yield x (EncodeState s xs)
4127+-------------------------------------------------------------------------------
4128+-- Concurrent application and fold
4129+-------------------------------------------------------------------------------
4131+-- Using StreamD the worker stream producing code can fuse with the code to
4132+-- queue output to the SVar giving some perf boost.
4134+-- Note that StreamD can only be used in limited situations, specifically, we
4135+-- cannot implement joinStreamVarPar using this.
4137+-- XXX make sure that the SVar passed is a Parallel style SVar.
4139+-- | Fold the supplied stream to the SVar asynchronously using Parallel
4140+-- concurrency style.
4141+-- {-# INLINE_NORMAL toSVarParallel #-}
4142+{-# INLINE toSVarParallel #-}
4143+toSVarParallel :: MonadAsync m
4144+ => State t m a -> SVar t m a -> Stream m a -> m ()
4145+toSVarParallel st sv xs =
4146+ if svarInspectMode sv
4150+ case getYieldLimit st of
4151+ Nothing -> doFork (work Nothing)
4153+ (handleChildException sv)
4154+ Just _ -> doFork (workLim Nothing)
4156+ (handleChildException sv)
4157+ modifyThread sv tid
4161+ {-# NOINLINE work #-}
4162+ work info = (runFold (FL.toParallelSVar sv info) xs)
4164+ {-# NOINLINE workLim #-}
4165+ workLim info = runFold (FL.toParallelSVarLimited sv info) xs
4167+ {-# NOINLINE forkWithDiag #-}
4169+ -- We do not use workerCount in case of ParallelVar but still there is
4170+ -- no harm in maintaining it correctly.
4171+ liftIO $ atomicModifyIORefCAS_ (workerCount sv) $ \n -> n + 1
4172+ recordMaxWorkers sv
4173+ -- This allocation matters when significant number of workers are being
4174+ -- sent. We allocate it only when needed. The overhead increases by 4x.
4176+ case yieldRateInfo sv of
4177+ Nothing -> return Nothing
4178+ Just _ -> liftIO $ do
4179+ cntRef <- newIORef 0
4180+ t <- getTime Monotonic
4181+ lat <- newIORef (0, t)
4182+ return $ Just WorkerInfo
4183+ { workerYieldMax = 0
4184+ , workerYieldCount = cntRef
4185+ , workerLatencyStart = lat
4188+ case getYieldLimit st of
4189+ Nothing -> doFork (work winfo)
4191+ (handleChildException sv)
4192+ Just _ -> doFork (workLim winfo)
4194+ (handleChildException sv)
4195+ modifyThread sv tid
4197+{-# INLINE_NORMAL mkParallel #-}
4198+mkParallel :: MonadAsync m => State t m a -> Stream m a -> m (Stream m a)
4199+mkParallel st m = do
4200+ sv <- newParallelVar StopNone defState
4201+ toSVarParallel st sv m
4202+ return $ fromSVar sv
4204+{-# INLINE_NORMAL applyParallel #-}
4205+applyParallel :: MonadAsync m
4206+ => (Stream m a -> Stream m b) -> Stream m a -> Stream m b
4207+applyParallel f m = Stream step Nothing
4210+ {-# INLINE_LATE step #-}
4211+ step gst Nothing = do
4212+ sv <- newParallelVar StopNone (adaptState gst)
4213+ toSVarParallel (adaptState gst) sv m
4214+ return $ Skip $ Just $ f (fromSVar sv)
4216+ step gst (Just (UnStream step1 st)) = do
4219+ Yield a s -> Yield a (Just $ Stream step1 s)
4220+ Skip s -> Skip (Just $ Stream step1 s)
4223+{-# INLINE foldParallel #-}
4224+foldParallel :: (K.IsStream t, MonadAsync m) => (t m a -> m b) -> t m a -> m b
4225+foldParallel f m = do
4226+ sv <- newParallelVar StopNone defState
4227+ toSVarParallel defState sv (toStreamD m)
4228+ f $ fromStreamD $ fromSVar sv