@@ -11,7 +11,7 @@
1111--
1212-- To run examples in this module:
1313--
14−-- >>> import qualified Streamly.Prelude as Stream
14+-- >>> import qualified Streamly.Data.Stream as Stream
1515-- >>> import Control.Concurrent (threadDelay)
1616-- >>> :{
1717-- delay n = do
@@ -42,10 +42,15 @@ module Streamly.Internal.Data.Stream.Parallel
4242
4343 -- * Callbacks
4444 , newCallbackStream
45+
46+ -- * Combinators
47+ , interjectSuffix
48+ , takeInterval
49+ , dropInterval
4550 )
4651where
4752
48−import Control.Concurrent (myThreadId, takeMVar)
53+import Control.Concurrent (myThreadId, takeMVar, threadDelay)
4954import Control.Monad (when)
5055import Control.Monad.Base (MonadBase(..), liftBaseDefault)
5156import Control.Monad.Catch (MonadThrow, throwM)
@@ -56,37 +61,36 @@ import Control.Monad.State.Class (MonadState(..))
5661import Control.Monad.Trans.Class (MonadTrans(lift))
5762import Data.Functor (void)
5863import Data.IORef (readIORef, writeIORef)
59−import Data.Maybe (fromJust)
64+import Data.Maybe (fromJust, isNothing)
6065#if __GLASGOW_HASKELL__ < 808
6166import Data.Semigroup (Semigroup(..))
6267#endif
63−import Prelude hiding (map)
64−
65−import qualified Data.Set as Set
6668
6769import Streamly.Data.Fold (Fold)
6870import Streamly.Internal.Control.Concurrent (MonadAsync)
6971import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
70−import Streamly.Internal.Data.Stream.StreamK.Type (Stream)
72+import Streamly.Internal.Data.Stream.Type (Stream)
7173
74+import qualified Data.Set as Set
7275import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
73− (foldStreamShared, mkStream, foldStream, fromEffect
76+ (Stream, foldStreamShared, mkStream, foldStream, fromEffect
7477 , nil, concatMapWith, fromPure, bindWith, withLocal)
7578import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
7679 (Stream(..), mapM, toStreamK, fromStreamK)
7780import qualified Streamly.Internal.Data.Stream.SVar.Generate as SVar
7881import qualified Streamly.Internal.Data.Stream.SVar.Eliminate as SVar
7982import qualified Streamly.Internal.Data.Stream as Stream
80− (toStreamK, fromStreamK)
83+ (catMaybes, dropWhile, fromStreamK, repeat, sequence, takeWhile, toStreamK)
8184
8285import Streamly.Internal.Data.SVar
86+import Prelude hiding (map)
8387
8488#include "inline.hs"
8589#include "Instances.hs"
8690
8791--
8892-- $setup
89−-- >>> import qualified Streamly.Prelude as Stream
93+-- >>> import qualified Streamly.Data.Stream as Stream
9094-- >>> import Control.Concurrent (threadDelay)
9195-- >>> :{
9296-- delay n = do
@@ -106,7 +110,7 @@ import Streamly.Internal.Data.SVar
106110{-# NOINLINE runOne #-}
107111runOne
108112 :: 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 ()
110114runOne st m0 winfo =
111115 case getYieldLimit st of
112116 Nothing -> go m0
@@ -129,7 +133,7 @@ runOne st m0 winfo =
129133
130134runOneLimited
131135 :: 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 ()
133137runOneLimited st m0 winfo = go m0
134138
135139 where
@@ -166,7 +170,7 @@ runOneLimited st m0 winfo = go m0
166170
167171{-# NOINLINE forkSVarPar #-}
168172forkSVarPar :: 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
170174forkSVarPar ss m r = K.mkStream $ \st yld sng stp -> do
171175 sv <- newParallelVar ss st
172176 pushWorkerPar sv (runOne st{streamVar = Just sv} m)
@@ -179,8 +183,13 @@ forkSVarPar ss m r = K.mkStream $ \st yld sng stp -> do
179183 K.foldStream st yld sng stp $ Stream.toStreamK (SVar.fromSVar sv)
180184
181185{-# INLINE joinStreamVarPar #-}
182−joinStreamVarPar :: MonadAsync m
183− => SVarStyle -> SVarStopStyle -> Stream m a -> Stream m a -> Stream m a
186+joinStreamVarPar ::
187+ MonadAsync m
188+ => SVarStyle
189+ -> SVarStopStyle
190+ -> K.Stream m a
191+ -> K.Stream m a
192+ -> K.Stream m a
184193joinStreamVarPar style ss m1 m2 = K.mkStream $ \st yld sng stp ->
185194 case streamVar st of
186195 Just sv | svarStyle sv == style && svarStopStyle sv == ss -> do
@@ -230,7 +239,7 @@ joinStreamVarPar style ss m1 m2 = K.mkStream $ \st yld sng stp ->
230239-------------------------------------------------------------------------------
231240
232241{-# 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
234243parallelK = joinStreamVarPar ParallelVar StopNone
235244
236245-- | 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
248257--
249258-- /Pre-release/
250259{-# 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
252261parallelFstK = joinStreamVarPar ParallelVar StopBy
253262
254263-- This is a race like combinator for streams.
@@ -258,7 +267,7 @@ parallelFstK = joinStreamVarPar ParallelVar StopBy
258267--
259268-- /Pre-release/
260269{-# 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
262271parallelMinK = joinStreamVarPar ParallelVar StopAny
263272
264273------------------------------------------------------------------------------
@@ -269,7 +278,7 @@ parallelMinK = joinStreamVarPar ParallelVar StopAny
269278--
270279-- /Pre-release/
271280--
272−mkParallelK :: MonadAsync m => Stream m a -> Stream m a
281+mkParallelK :: MonadAsync m => K.Stream m a -> K.Stream m a
273282mkParallelK m = K.mkStream $ \st yld sng stp -> do
274283 sv <- newParallelVar StopNone (adaptState st)
275284 -- pushWorkerPar sv (runOne st{streamVar = Just sv} $ toStream m)
@@ -342,7 +351,8 @@ mkParallelD m = D.Stream step Nothing
342351--
343352-- /Pre-release/
344353{-# INLINE tapAsyncK #-}
345−tapAsyncK :: MonadAsync m => (Stream m a -> m b) -> Stream m a -> Stream m a
354+tapAsyncK ::
355+ MonadAsync m => (K.Stream m a -> m b) -> K.Stream m a -> K.Stream m a
346356tapAsyncK f m = K.mkStream $ \st yld sng stp -> do
347357 sv <- SVar.newFoldSVar st (f . Stream.toStreamK)
348358 K.foldStreamShared st yld sng stp
@@ -420,7 +430,7 @@ tapAsyncF f (D.Stream step1 state1) = D.Stream step TapInit
420430-- /Since: 0.7.0 (maxBuffer applies to ParallelT streams)/
421431--
422432-- @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}
424434 deriving (MonadTrans)
425435
426436-- | A parallely composing IO stream of elements of type @a@.
@@ -507,7 +517,7 @@ MONAD_COMMON_INSTANCES(ParallelT, MONADPARALLEL)
507517-- /Pre-release/
508518--
509519{-# INLINE_NORMAL newCallbackStream #-}
510−newCallbackStream :: MonadAsync m => m (a -> m (), Stream m a)
520+newCallbackStream :: MonadAsync m => m (a -> m (), K.Stream m a)
511521newCallbackStream = do
512522 sv <- newParallelVar StopNone defState
513523
@@ -520,3 +530,87 @@ newCallbackStream = do
520530 -- XXX we can return an SVar and then the consumer can unfold from the
521531 -- SVar?
522532 return (callback, D.toStreamK (SVar.fromSVarD sv))
533+
534+------------------------------------------------------------------------------
535+-- Combinators
536+------------------------------------------------------------------------------
537+
538+{-# INLINE parallelFst #-}
539+parallelFst :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
540+parallelFst m1 m2 =
541+ Stream.fromStreamK
542+ $ parallelFstK (Stream.toStreamK m1) (Stream.toStreamK m2)
543+
544+-- | Intersperse a monadic action into the input stream after every @n@
545+-- seconds.
546+--
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"
550+-- h,e,l,l,o
551+--
552+-- /Pre-release/
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
558+
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
562+-- options:
563+--
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.
566+--
567+-- 2) Use COARSE clock to get time with lower resolution but more efficiently.
568+--
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.
572+
573+
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
578+-- stream stops.
579+--
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.
584+--
585+-- No element is yielded if the duration is zero. At least one element is
586+-- yielded if the duration is non-zero.
587+--
588+-- /Pre-release/
589+--
590+{-# INLINE takeInterval #-}
591+takeInterval :: MonadAsync m => Double -> Stream m a -> Stream m a
592+takeInterval d =
593+ Stream.catMaybes
594+ . Stream.takeWhile isNothing
595+ . interjectSuffix d (return Nothing) . fmap Just
596+
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.
601+--
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.
606+--
607+-- All elements are yielded if the duration is zero.
608+--
609+-- /Pre-release/
610+--
611+{-# INLINE dropInterval #-}
612+dropInterval :: MonadAsync m => Double -> Stream m a -> Stream m a
613+dropInterval d =
614+ Stream.catMaybes
615+ . Stream.dropWhile isNothing
616+ . interjectSuffix d (return Nothing) . fmap Just