@@ -21,10 +21,19 @@ module Streamly.Internal.Data.Stream.StreamD.Type
21 , fromStreamK
21 , fromStreamK
22 , toStreamK
22 , toStreamK
23 , fromStreamD
23 , fromStreamD
25− , mapM
25+ , fromProducer
28 , concatMap
37 , concatMap
29 , concatMapM
38 , concatMapM
@@ -45,6 +54,8 @@ module Streamly.Internal.Data.Stream.StreamD.Type
48 , GroupState (..) -- for inspection testing
59 , GroupState (..) -- for inspection testing
49 , foldMany
60 , foldMany
50 , foldMany1
61 , foldMany1
@@ -52,21 +63,31 @@ module Streamly.Internal.Data.Stream.StreamD.Type
55import Control.Applicative (liftA2)
67import Control.Applicative (liftA2)
68+import Control.Exception (fromException)
56import Control.Monad (when)
69import Control.Monad (when)
57import Control.Monad.Catch (MonadThrow, throwM)
70import Control.Monad.Catch (MonadThrow, throwM)
71+import Control.Monad.IO.Class (MonadIO(..))
58import Control.Monad.Trans.Class (lift, MonadTrans)
72import Control.Monad.Trans.Class (lift, MonadTrans)
73+import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
59import Data.Functor.Identity (Identity(..))
74import Data.Functor.Identity (Identity(..))
75+import Data.Maybe (isNothing)
76+import Fusion.Plugin.Types (Fuse(..))
60import GHC.Base (build)
77import GHC.Base (build)
61import GHC.Types (SPEC(..))
78import GHC.Types (SPEC(..))
62−import Prelude hiding (map, mapM, foldr, take, concatMap)
79+import Prelude hiding (map, mapM, foldr, take, concatMap, takeWhile)
63−import Fusion.Plugin.Types (Fuse(..))
80+import System.Mem (performMajorGC)
65−import Streamly.Internal.Data.SVar (State(..), adaptState, defState)
82+import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
83+import Streamly.Internal.Data.IORef.Prim (Prim)
84+import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
66import Streamly.Internal.Data.Fold.Types (Fold(..), Fold2(..))
85import Streamly.Internal.Data.Fold.Types (Fold(..), Fold2(..))
86+import Streamly.Internal.Data.SVar
68−import qualified Streamly.Internal.Data.Fold.Types as FL
88+import qualified Streamly.Internal.Data.Fold as FL
69import qualified Streamly.Internal.Data.Stream.StreamK as K
89import qualified Streamly.Internal.Data.Stream.StreamK as K
90+import qualified Streamly.Internal.Data.IORef.Prim as Prim
71------------------------------------------------------------------------------
92------------------------------------------------------------------------------
72-- The direct style stream type
93-- The direct style stream type
@@ -141,6 +162,49 @@ toStreamK (Stream step state) = go state
141fromStreamD :: (K.IsStream t, Monad m) => Stream m a -> t m a
162fromStreamD :: (K.IsStream t, Monad m) => Stream m a -> t m a
142fromStreamD = K.fromStream . toStreamK
163fromStreamD = K.fromStream . toStreamK
165+{-# INLINE toStreamD #-}
166+toStreamD :: (K.IsStream t, Monad m) => t m a -> Stream m a
167+toStreamD = fromStreamK . K.toStream
169+-------------------------------------------------------------------------------
171+-------------------------------------------------------------------------------
173+-- Does not fuse, has the same performance as the StreamK version.
174+{-# INLINE_NORMAL uncons #-}
175+uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))
176+uncons (UnStream step state) = go state
179+ r <- step defState st
181+ Yield x s -> return $ Just (x, Stream step s)
183+ Stop -> return Nothing
185+-------------------------------------------------------------------------------
187+-------------------------------------------------------------------------------
189+-- | An empty 'Stream' with a side effect.
190+{-# INLINE_NORMAL nilM #-}
191+nilM :: Monad m => m b -> Stream m a
192+nilM m = Stream (\_ _ -> m >> return Stop) ()
194+{-# INLINE_NORMAL consM #-}
195+consM :: Monad m => m a -> Stream m a -> Stream m a
196+consM m (Stream step state) = Stream step1 Nothing
198+ {-# INLINE_LATE step1 #-}
199+ step1 _ Nothing = m >>= \x -> return $ Yield x (Just state)
200+ step1 gst (Just st) = do
204+ Yield a s -> Yield a (Just s)
205+ Skip s -> Skip (Just s)
144------------------------------------------------------------------------------
208------------------------------------------------------------------------------
145-- Instances
209-- Instances
146------------------------------------------------------------------------------
210------------------------------------------------------------------------------
@@ -567,7 +631,9 @@ cmpBy cmp (Stream step1 t1) (Stream step2 t2) = cmp_loop0 SPEC t1 t2
567 Skip s2' -> cmp_null s2'
631 Skip s2' -> cmp_null s2'
568 Stop -> return EQ
632 Stop -> return EQ
570−-- XXX The take/drop combinators above should be moved to filtering section.
634+-------------------------------------------------------------------------------
636+-------------------------------------------------------------------------------
572{-# INLINE_NORMAL take #-}
638{-# INLINE_NORMAL take #-}
573take :: Monad m => Int -> Stream m a -> Stream m a
639take :: Monad m => Int -> Stream m a -> Stream m a
@@ -582,6 +648,25 @@ take n (Stream step state) = n `seq` Stream step' (state, 0)
582 Stop -> Stop
648 Stop -> Stop
583 step' _ (_, _) = return Stop
649 step' _ (_, _) = return Stop
652+{-# INLINE_NORMAL takeWhileM #-}
653+takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
654+takeWhileM f (Stream step state) = Stream step' state
656+ {-# INLINE_LATE step' #-}
662+ return $ if b then Yield x s else Stop
663+ Skip s -> return $ Skip s
666+{-# INLINE takeWhile #-}
667+takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
668+takeWhile f = takeWhileM (return . f)
585------------------------------------------------------------------------------
670------------------------------------------------------------------------------
586-- Grouping/Splitting
671-- Grouping/Splitting
587------------------------------------------------------------------------------
672------------------------------------------------------------------------------
@@ -706,3 +791,228 @@ groupsOf2 n input (Fold2 fstep inject extract) (Stream step state) =
706 return $ Yield r next
791 return $ Yield r next
708 step' _ GroupFinish2 = return Stop
793 step' _ GroupFinish2 = return Stop
795+data FromSVarState t m a =
797+ | FromSVarRead (SVar t m a)
798+ | FromSVarLoop (SVar t m a) [ChildEvent a]
799+ | FromSVarDone (SVar t m a)
801+-------------------------------------------------------------------------------
802+-- Generation from SVar
803+-------------------------------------------------------------------------------
805+{-# INLINE_NORMAL fromSVar #-}
806+fromSVar :: (MonadAsync m) => SVar t m a -> Stream m a
807+fromSVar svar = Stream step FromSVarInit
810+ {-# INLINE_LATE step #-}
811+ step _ FromSVarInit = do
812+ ref <- liftIO $ newIORef ()
813+ _ <- liftIO $ mkWeakIORef ref hook
814+ -- when this copy of svar gets garbage collected "ref" will get
815+ -- garbage collected and our GC hook will be called.
816+ let sv = svar{svarRef = Just ref}
817+ return $ Skip (FromSVarRead sv)
821+ {-# NOINLINE hook #-}
823+ when (svarInspectMode svar) $ do
824+ r <- liftIO $ readIORef (svarStopTime (svarStats svar))
825+ when (isNothing r) $
826+ printSVar svar "SVar Garbage Collected"
828+ -- If there are any SVars referenced by this SVar a GC will prompt
829+ -- them to be cleaned up quickly.
830+ when (svarInspectMode svar) performMajorGC
832+ step _ (FromSVarRead sv) = do
833+ list <- readOutputQ sv
834+ -- Reversing the output is important to guarantee that we process the
835+ -- outputs in the same order as they were generated by the constituent
837+ return $ Skip $ FromSVarLoop sv (Prelude.reverse list)
839+ step _ (FromSVarLoop sv []) = do
840+ done <- postProcess sv
841+ return $ Skip $ if done
842+ then (FromSVarDone sv)
843+ else (FromSVarRead sv)
845+ step _ (FromSVarLoop sv (ev : es)) = do
847+ ChildYield a -> return $ Yield a (FromSVarLoop sv es)
848+ ChildStop tid e -> do
849+ accountThread sv tid
852+ stop <- shouldStop tid
855+ liftIO (cleanupSVar sv)
856+ return $ Skip (FromSVarDone sv)
857+ else return $ Skip (FromSVarLoop sv es)
859+ case fromException ex of
861+ return $ Skip (FromSVarLoop sv es)
862+ Nothing -> liftIO (cleanupSVar sv) >> throwM ex
866+ case svarStopStyle sv of
867+ StopNone -> return False
868+ StopAny -> return True
870+ sid <- liftIO $ readIORef (svarStopBy sv)
871+ return $ if tid == sid then True else False
873+ step _ (FromSVarDone sv) = do
874+ when (svarInspectMode sv) $ do
875+ t <- liftIO $ getTime Monotonic
876+ liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
877+ liftIO $ printSVar sv "SVar Done"
880+{-# INLINE_NORMAL fromPrimIORef #-}
881+fromPrimIORef :: (MonadIO m, Prim a) => Prim.IORef a -> Stream m a
882+fromPrimIORef var = Stream step ()
884+ {-# INLINE_LATE step #-}
885+ step _ () = liftIO (Prim.readIORef var) >>= \x -> return $ Yield x ()
887+-------------------------------------------------------------------------------
888+-- Process events received by a fold consumer from a stream producer
889+-------------------------------------------------------------------------------
891+{-# INLINE_NORMAL fromProducer #-}
892+fromProducer :: (MonadAsync m) => SVar t m a -> Stream m a
893+fromProducer svar = Stream step (FromSVarRead svar)
896+ {-# INLINE_LATE step #-}
897+ step _ (FromSVarRead sv) = do
898+ list <- readOutputQ sv
899+ -- Reversing the output is important to guarantee that we process the
900+ -- outputs in the same order as they were generated by the constituent
902+ return $ Skip $ FromSVarLoop sv (Prelude.reverse list)
904+ step _ (FromSVarLoop sv []) = return $ Skip $ FromSVarRead sv
905+ step _ (FromSVarLoop sv (ev : es)) = do
907+ ChildYield a -> return $ Yield a (FromSVarLoop sv es)
908+ ChildStop tid e -> do
909+ accountThread sv tid
912+ sendStopToProducer sv
913+ return $ Skip (FromSVarDone sv)
914+ Just _ -> error "Bug: fromProducer: received exception"
916+ step _ (FromSVarDone sv) = do
917+ when (svarInspectMode sv) $ do
918+ t <- liftIO $ getTime Monotonic
919+ liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
920+ liftIO $ printSVar sv "SVar Done"
923+ step _ FromSVarInit = undefined
925+------------------------------------------------------------------------------
927+------------------------------------------------------------------------------
929+{-# INLINE_NORMAL foldOnce #-}
930+foldOnce :: (Monad m) => Fold m a b -> Stream m a -> m b
931+foldOnce (Fold fstep begin done) (Stream step state) =
932+ begin >>= \x -> go SPEC x state
938+ r <- step defState st
943+ FL.Done b -> return b
944+ FL.Partial fs1 -> go SPEC fs1 s
945+ Skip s -> go SPEC fs s
948+-------------------------------------------------------------------------------
949+-- Concurrent application and fold
950+-------------------------------------------------------------------------------
952+-- XXX These functions should be moved to Stream/Parallel.hs
954+-- Using StreamD the worker stream producing code can fuse with the code to
955+-- queue output to the SVar giving some perf boost.
957+-- Note that StreamD can only be used in limited situations, specifically, we
958+-- cannot implement joinStreamVarPar using this.
960+-- XXX make sure that the SVar passed is a Parallel style SVar.
962+-- | Fold the supplied stream to the SVar asynchronously using Parallel
963+-- concurrency style.
964+-- {-# INLINE_NORMAL toSVarParallel #-}
965+{-# INLINE toSVarParallel #-}
966+toSVarParallel :: MonadAsync m
967+ => State t m a -> SVar t m a -> Stream m a -> m ()
968+toSVarParallel st sv xs =
969+ if svarInspectMode sv
973+ case getYieldLimit st of
974+ Nothing -> doFork (work Nothing)
976+ (handleChildException sv)
977+ Just _ -> doFork (workLim Nothing)
979+ (handleChildException sv)
984+ {-# NOINLINE work #-}
985+ work info = (foldOnce (FL.toParallelSVar sv info) xs)
987+ {-# NOINLINE workLim #-}
988+ workLim info = foldOnce (FL.toParallelSVarLimited sv info) xs
990+ {-# NOINLINE forkWithDiag #-}
992+ -- We do not use workerCount in case of ParallelVar but still there is
993+ -- no harm in maintaining it correctly.
994+ liftIO $ atomicModifyIORefCAS_ (workerCount sv) $ \n -> n + 1
996+ -- This allocation matters when significant number of workers are being
997+ -- sent. We allocate it only when needed. The overhead increases by 4x.
999+ case yieldRateInfo sv of
1000+ Nothing -> return Nothing
1001+ Just _ -> liftIO $ do
1002+ cntRef <- newIORef 0
1003+ t <- getTime Monotonic
1004+ lat <- newIORef (0, t)
1005+ return $ Just WorkerInfo
1006+ { workerYieldMax = 0
1007+ , workerYieldCount = cntRef
1008+ , workerLatencyStart = lat
1011+ case getYieldLimit st of
1012+ Nothing -> doFork (work winfo)
1014+ (handleChildException sv)
1015+ Just _ -> doFork (workLim winfo)
1017+ (handleChildException sv)
1018+ modifyThread sv tid