@@ -11,7 +11,7 @@
12-- To run examples in this module:
12-- To run examples in this module:
14−-- >>> import qualified Streamly.Prelude as Stream
14+-- >>> import qualified Streamly.Data.Stream as Stream
15-- >>> import Control.Concurrent (threadDelay)
15-- >>> import Control.Concurrent (threadDelay)
17-- delay n = do
17-- delay n = do
@@ -42,10 +42,15 @@ module Streamly.Internal.Data.Stream.Parallel
43 -- * Callbacks
43 -- * Callbacks
44 , newCallbackStream
44 , newCallbackStream
48−import Control.Concurrent (myThreadId, takeMVar)
53+import Control.Concurrent (myThreadId, takeMVar, threadDelay)
49import Control.Monad (when)
54import Control.Monad (when)
50import Control.Monad.Base (MonadBase(..), liftBaseDefault)
55import Control.Monad.Base (MonadBase(..), liftBaseDefault)
51import Control.Monad.Catch (MonadThrow, throwM)
56import Control.Monad.Catch (MonadThrow, throwM)
@@ -56,37 +61,36 @@ import Control.Monad.State.Class (MonadState(..))
56import Control.Monad.Trans.Class (MonadTrans(lift))
61import Control.Monad.Trans.Class (MonadTrans(lift))
57import Data.Functor (void)
62import Data.Functor (void)
58import Data.IORef (readIORef, writeIORef)
63import Data.IORef (readIORef, writeIORef)
59−import Data.Maybe (fromJust)
64+import Data.Maybe (fromJust, isNothing)
60#if __GLASGOW_HASKELL__ < 808
65#if __GLASGOW_HASKELL__ < 808
61import Data.Semigroup (Semigroup(..))
66import Data.Semigroup (Semigroup(..))
63−import Prelude hiding (map)
65−import qualified Data.Set as Set
67import Streamly.Data.Fold (Fold)
69import Streamly.Data.Fold (Fold)
68import Streamly.Internal.Control.Concurrent (MonadAsync)
70import Streamly.Internal.Control.Concurrent (MonadAsync)
69import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
71import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
70−import Streamly.Internal.Data.Stream.StreamK.Type (Stream)
72+import Streamly.Internal.Data.Stream.Type (Stream)
74+import qualified Data.Set as Set
72import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
75import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
73− (foldStreamShared, mkStream, foldStream, fromEffect
76+ (Stream, foldStreamShared, mkStream, foldStream, fromEffect
74 , nil, concatMapWith, fromPure, bindWith, withLocal)
77 , nil, concatMapWith, fromPure, bindWith, withLocal)
75import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
78import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
76 (Stream(..), mapM, toStreamK, fromStreamK)
79 (Stream(..), mapM, toStreamK, fromStreamK)
77import qualified Streamly.Internal.Data.Stream.SVar.Generate as SVar
80import qualified Streamly.Internal.Data.Stream.SVar.Generate as SVar
78import qualified Streamly.Internal.Data.Stream.SVar.Eliminate as SVar
81import qualified Streamly.Internal.Data.Stream.SVar.Eliminate as SVar
79import qualified Streamly.Internal.Data.Stream as Stream
82import qualified Streamly.Internal.Data.Stream as Stream
80− (toStreamK, fromStreamK)
83+ (catMaybes, dropWhile, fromStreamK, repeat, sequence, takeWhile, toStreamK)
82import Streamly.Internal.Data.SVar
85import Streamly.Internal.Data.SVar
86+import Prelude hiding (map)
84#include "inline.hs"
88#include "inline.hs"
85#include "Instances.hs"
89#include "Instances.hs"
89−-- >>> import qualified Streamly.Prelude as Stream
93+-- >>> import qualified Streamly.Data.Stream as Stream
90-- >>> import Control.Concurrent (threadDelay)
94-- >>> import Control.Concurrent (threadDelay)
92-- delay n = do
96-- delay n = do
@@ -106,7 +110,7 @@ import Streamly.Internal.Data.SVar
106{-# NOINLINE runOne #-}
110{-# NOINLINE runOne #-}
108 :: MonadIO m
112 :: MonadIO m
109− => State Stream m a -> Stream m a -> Maybe WorkerInfo -> m ()
113+ => State K.Stream m a -> K.Stream m a -> Maybe WorkerInfo -> m ()
110runOne st m0 winfo =
114runOne st m0 winfo =
111 case getYieldLimit st of
115 case getYieldLimit st of
112 Nothing -> go m0
116 Nothing -> go m0
@@ -129,7 +133,7 @@ runOne st m0 winfo =
130runOneLimited
134runOneLimited
131 :: MonadIO m
135 :: MonadIO m
132− => State Stream m a -> Stream m a -> Maybe WorkerInfo -> m ()
136+ => State K.Stream m a -> K.Stream m a -> Maybe WorkerInfo -> m ()
133runOneLimited st m0 winfo = go m0
137runOneLimited st m0 winfo = go m0
@@ -166,7 +170,7 @@ runOneLimited st m0 winfo = go m0
167{-# NOINLINE forkSVarPar #-}
171{-# NOINLINE forkSVarPar #-}
168forkSVarPar :: MonadAsync m
172forkSVarPar :: MonadAsync m
169− => SVarStopStyle -> Stream m a -> Stream m a -> Stream m a
173+ => SVarStopStyle -> K.Stream m a -> K.Stream m a -> K.Stream m a
170forkSVarPar ss m r = K.mkStream $ \st yld sng stp -> do
174forkSVarPar ss m r = K.mkStream $ \st yld sng stp -> do
171 sv <- newParallelVar ss st
175 sv <- newParallelVar ss st
172 pushWorkerPar sv (runOne st{streamVar = Just sv} m)
176 pushWorkerPar sv (runOne st{streamVar = Just sv} m)
@@ -179,8 +183,13 @@ forkSVarPar ss m r = K.mkStream $ \st yld sng stp -> do
179 K.foldStream st yld sng stp $ Stream.toStreamK (SVar.fromSVar sv)
183 K.foldStream st yld sng stp $ Stream.toStreamK (SVar.fromSVar sv)
181{-# INLINE joinStreamVarPar #-}
185{-# INLINE joinStreamVarPar #-}
182−joinStreamVarPar :: MonadAsync m
186+joinStreamVarPar ::
183− => SVarStyle -> SVarStopStyle -> Stream m a -> Stream m a -> Stream m a
189+ -> SVarStopStyle
184joinStreamVarPar style ss m1 m2 = K.mkStream $ \st yld sng stp ->
193joinStreamVarPar style ss m1 m2 = K.mkStream $ \st yld sng stp ->
185 case streamVar st of
194 case streamVar st of
186 Just sv | svarStyle sv == style && svarStopStyle sv == ss -> do
195 Just sv | svarStyle sv == style && svarStopStyle sv == ss -> do
@@ -230,7 +239,7 @@ joinStreamVarPar style ss m1 m2 = K.mkStream $ \st yld sng stp ->
230-------------------------------------------------------------------------------
239-------------------------------------------------------------------------------
232{-# INLINE parallelK #-}
241{-# INLINE parallelK #-}
233−parallelK :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
242+parallelK :: MonadAsync m => K.Stream m a -> K.Stream m a -> K.Stream m a
234parallelK = joinStreamVarPar ParallelVar StopNone
243parallelK = joinStreamVarPar ParallelVar StopNone
236-- | XXX we can implement it more efficienty by directly implementing instead
245-- | XXX we can implement it more efficienty by directly implementing instead
@@ -248,7 +257,7 @@ consM m (ParallelT r) = ParallelT $ parallelK (K.fromEffect m) r
249-- /Pre-release/
258-- /Pre-release/
250{-# INLINE parallelFstK #-}
259{-# INLINE parallelFstK #-}
251−parallelFstK :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
260+parallelFstK :: MonadAsync m => K.Stream m a -> K.Stream m a -> K.Stream m a
252parallelFstK = joinStreamVarPar ParallelVar StopBy
261parallelFstK = joinStreamVarPar ParallelVar StopBy
254-- This is a race like combinator for streams.
263-- This is a race like combinator for streams.
@@ -258,7 +267,7 @@ parallelFstK = joinStreamVarPar ParallelVar StopBy
259-- /Pre-release/
268-- /Pre-release/
260{-# INLINE parallelMinK #-}
269{-# INLINE parallelMinK #-}
261−parallelMinK :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
270+parallelMinK :: MonadAsync m => K.Stream m a -> K.Stream m a -> K.Stream m a
262parallelMinK = joinStreamVarPar ParallelVar StopAny
271parallelMinK = joinStreamVarPar ParallelVar StopAny
264------------------------------------------------------------------------------
273------------------------------------------------------------------------------
@@ -269,7 +278,7 @@ parallelMinK = joinStreamVarPar ParallelVar StopAny
270-- /Pre-release/
279-- /Pre-release/
272−mkParallelK :: MonadAsync m => Stream m a -> Stream m a
281+mkParallelK :: MonadAsync m => K.Stream m a -> K.Stream m a
273mkParallelK m = K.mkStream $ \st yld sng stp -> do
282mkParallelK m = K.mkStream $ \st yld sng stp -> do
274 sv <- newParallelVar StopNone (adaptState st)
283 sv <- newParallelVar StopNone (adaptState st)
275 -- pushWorkerPar sv (runOne st{streamVar = Just sv} $ toStream m)
284 -- pushWorkerPar sv (runOne st{streamVar = Just sv} $ toStream m)
@@ -342,7 +351,8 @@ mkParallelD m = D.Stream step Nothing
343-- /Pre-release/
352-- /Pre-release/
344{-# INLINE tapAsyncK #-}
353{-# INLINE tapAsyncK #-}
345−tapAsyncK :: MonadAsync m => (Stream m a -> m b) -> Stream m a -> Stream m a
355+ MonadAsync m => (K.Stream m a -> m b) -> K.Stream m a -> K.Stream m a
346tapAsyncK f m = K.mkStream $ \st yld sng stp -> do
356tapAsyncK f m = K.mkStream $ \st yld sng stp -> do
347 sv <- SVar.newFoldSVar st (f . Stream.toStreamK)
357 sv <- SVar.newFoldSVar st (f . Stream.toStreamK)
348 K.foldStreamShared st yld sng stp
358 K.foldStreamShared st yld sng stp
@@ -420,7 +430,7 @@ tapAsyncF f (D.Stream step1 state1) = D.Stream step TapInit
420-- /Since: 0.7.0 (maxBuffer applies to ParallelT streams)/
430-- /Since: 0.7.0 (maxBuffer applies to ParallelT streams)/
422-- @since 0.8.0
432-- @since 0.8.0
423−newtype ParallelT m a = ParallelT {getParallelT :: Stream m a}
433+newtype ParallelT m a = ParallelT {getParallelT :: K.Stream m a}
424 deriving (MonadTrans)
434 deriving (MonadTrans)
426-- | A parallely composing IO stream of elements of type @a@.
436-- | A parallely composing IO stream of elements of type @a@.
@@ -507,7 +517,7 @@ MONAD_COMMON_INSTANCES(ParallelT, MONADPARALLEL)
507-- /Pre-release/
517-- /Pre-release/
509{-# INLINE_NORMAL newCallbackStream #-}
519{-# INLINE_NORMAL newCallbackStream #-}
510−newCallbackStream :: MonadAsync m => m (a -> m (), Stream m a)
520+newCallbackStream :: MonadAsync m => m (a -> m (), K.Stream m a)
511newCallbackStream = do
521newCallbackStream = do
512 sv <- newParallelVar StopNone defState
522 sv <- newParallelVar StopNone defState
@@ -520,3 +530,87 @@ newCallbackStream = do
520 -- XXX we can return an SVar and then the consumer can unfold from the
530 -- XXX we can return an SVar and then the consumer can unfold from the
522 return (callback, D.toStreamK (SVar.fromSVarD sv))
532 return (callback, D.toStreamK (SVar.fromSVarD sv))
534+------------------------------------------------------------------------------
536+------------------------------------------------------------------------------
538+{-# INLINE parallelFst #-}
539+parallelFst :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
542+ $ parallelFstK (Stream.toStreamK m1) (Stream.toStreamK m2)
544+-- | Intersperse a monadic action into the input stream after every @n@
547+-- >>> import qualified Streamly.Data.Fold as Fold
548+-- >>> import qualified Streamly.Internal.Data.Stream.Parallel as Parallel
549+-- >>> Stream.fold Fold.drain $ Parallel.interjectSuffix 1.05 (putChar ',') $ Stream.mapM (\x -> threadDelay 1000000 >> putChar x) $ Stream.fromList "hello"
553+{-# INLINE interjectSuffix #-}
554+interjectSuffix :: MonadAsync m => Double -> m a -> Stream m a -> Stream m a
555+interjectSuffix n f xs = xs `parallelFst` repeatM timed
556+ where timed = liftIO (threadDelay (round $ n * 1000000)) >> f
557+ repeatM = Stream.sequence . Stream.repeat
559+-- XXX Notes from D.takeByTime (which was removed)
560+-- XXX using getTime in the loop can be pretty expensive especially for
561+-- computations where iterations are lightweight. We have the following
564+-- 1) Run a timeout thread updating a flag asynchronously and check that
565+-- flag here, that way we can have a cheap termination check.
567+-- 2) Use COARSE clock to get time with lower resolution but more efficiently.
569+-- 3) Use rdtscp/rdtsc to get time directly from the processor, compute the
570+-- termination value of rdtsc in the beginning and then in each iteration just
571+-- get rdtsc and check if we should terminate.
574+-- | @takeInterval duration@ yields stream elements upto specified time
575+-- @duration@ in seconds. The duration starts when the stream is evaluated for
576+-- the first time, before the first element is yielded. The time duration is
577+-- checked before generating each element, if the duration has expired the
580+-- The total time taken in executing the stream is guaranteed to be /at least/
581+-- @duration@, however, because the duration is checked before generating an
582+-- element, the upper bound is indeterminate and depends on the time taken in
583+-- generating and processing the last element.
585+-- No element is yielded if the duration is zero. At least one element is
586+-- yielded if the duration is non-zero.
590+{-# INLINE takeInterval #-}
591+takeInterval :: MonadAsync m => Double -> Stream m a -> Stream m a
594+ . Stream.takeWhile isNothing
595+ . interjectSuffix d (return Nothing) . fmap Just
597+-- | @dropInterval duration@ drops stream elements until specified @duration@ in
598+-- seconds has passed. The duration begins when the stream is evaluated for the
599+-- first time. The time duration is checked /after/ generating a stream element,
600+-- the element is yielded if the duration has expired otherwise it is dropped.
602+-- The time elapsed before starting to generate the first element is /at most/
603+-- @duration@, however, because the duration expiry is checked after the
604+-- element is generated, the lower bound is indeterminate and depends on the
605+-- time taken in generating an element.
607+-- All elements are yielded if the duration is zero.
611+{-# INLINE dropInterval #-}
612+dropInterval :: MonadAsync m => Double -> Stream m a -> Stream m a
615+ . Stream.dropWhile isNothing
616+ . interjectSuffix d (return Nothing) . fmap Just