on f7f264f help sign in
887 lines 29 kB history raw
1-- |
2-- Module : Streamly.Internal.Data.Stream.Concurrent
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
7-- Portability : GHC
8--
9-- Non-parallelizable stream combinators like unfoldrM, iterateM etc. can be
10-- evaluated concurrently with the stream consumer by using `eval`.
11-- Parallelizable combinators like repeatM, replicateM can generate the stream
12-- concurrently using 'concatMap'.
14-- Single effects related functionality can be moved to
15-- Data.Async/Control.Async.
16-- Common Channel functionality to Data.Channel.
17-- Stream channel to Data.Stream.Channel.
19module Streamly.Internal.Data.Stream.Concurrent
20 (
21 -- * Imports
22 -- $setup
24 -- * Types
25 MonadAsync
27 -- * Configuration
28 , Config
29 , maxThreads
30 , maxBuffer
31 , eager
32 , StopWhen (..)
33 , stopWhen
34 , ordered
35 , interleaved
36 -- maxYields
37 , Rate(..)
38 , rate
39 , avgRate
40 , minRate
41 , maxRate
42 , constRate
43 , inspect
45 -- * Combinators
46 -- | Stream combinators using a concurrent channel
48 -- ** Evaluate
49 -- | Evaluates a stream concurrently using a channel.
50 , eval
51 , parEval
52 -- Add unfoldrM/iterateM?
54 -- ** Generate
55 -- | Uses a single channel to evaluate all actions.
56 , repeatM
57 , parRepeatM
58 , replicateM
59 , parReplicateM
61 -- ** Map
62 -- | Uses a single channel to evaluate all actions.
63 , mapM
64 , parMapM
65 , sequence
66 , parSequence
68 -- ** Combine two
69 -- | Use a channel for each pair.
70 -- combine/concur/conjoin
71 , append2
72 , interleave2
73 , ahead2
74 , parallel2
75 , parallelFst2
76 , parallelMin2
77 , combineWith
79 -- ** List of streams
80 -- | Shares a single channel across many streams.
81 , parLazy
82 , parLazyOrdered
83 , parLazyInterleaved
84 , parEager
85 , parEagerFst
86 , parEagerMin
87 , parConcatList
89 , parZipWithM
90 , parZipWith
92 -- ** Stream of streams
93 -- *** Apply
94 , apply
95 , parApply
97 -- *** Concat
98 -- | Shares a single channel across many streams.
99 , concat
100 , parConcat
101 , concatMap
102 , concatMapInterleave
103 , parConcatMap
105 -- *** ConcatIterate
106 , parConcatIterate
108 -- ** Reactive
109 , fromCallback
110 , tapCountD
111 , tapCount
112 )
113where
115#include "inline.hs"
117import Control.Concurrent (myThreadId, killThread)
118import Control.Monad (void, when)
119import Control.Monad.IO.Class (MonadIO(liftIO))
120import Streamly.Internal.Control.Concurrent (MonadAsync, askRunInIO)
121import Streamly.Internal.Control.ForkLifted (forkManaged)
122import Streamly.Internal.Data.Stream.Channel.Dispatcher (modifyThread)
123import Streamly.Internal.Data.Stream.Channel.Types
124 ( ChildEvent(..)
125 , concatMapDivK
126 )
127import Streamly.Internal.Data.Stream.Channel.Worker (sendWithDoorBell)
128import Streamly.Internal.Data.Stream.Type (Stream)
129import Streamly.Internal.Data.Stream.StreamD (Step(..))
131import qualified Streamly.Internal.Data.IORef.Unboxed as Unboxed
132import qualified Streamly.Internal.Data.Stream as Stream
133import qualified Streamly.Internal.Data.Stream.StreamD as D
134import qualified Streamly.Internal.Data.Stream.StreamK as K
135import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
137import Prelude hiding (mapM, sequence, concat, concatMap, zipWith)
138import Streamly.Internal.Data.Stream.Concurrent.Channel
140-- $setup
141--
142-- Imports for example snippets in this module.
143--
144-- >>> :m
145-- >>> {-# LANGUAGE FlexibleContexts #-}
146-- >>> import Control.Concurrent (threadDelay)
147-- >>> import qualified Streamly.Data.Array as Array
148-- >>> import qualified Streamly.Data.Fold as Fold
149-- >>> import qualified Streamly.Data.Parser as Parser
150-- >>> import qualified Streamly.Internal.Data.Stream as Stream hiding (append2)
151-- >>> import qualified Streamly.Internal.Data.Stream.Concurrent as Stream
152-- >>> import Prelude hiding (concatMap, concat, zipWith)
153-- >>> :{
154-- delay n = do
155-- threadDelay (n * 1000000) -- sleep for n seconds
156-- putStrLn (show n ++ " sec") -- print "n sec"
157-- return n -- IO Int
158-- :}
160-------------------------------------------------------------------------------
161-- Evaluating a stream
162-------------------------------------------------------------------------------
164{-
165{-# INLINE_NORMAL parEvalD #-}
166parEvalD :: MonadAsync m => (Config -> Config) -> D.Stream m a -> D.Stream m a
167parEvalD modifier m = D.Stream step Nothing
168 where
170 step _ Nothing = do
171 chan <- newChannel modifier
172 sendFirstWorker chan (D.toStreamK m)
173 -- XXX should use an unfold to make this efficient
174 return $ D.Skip $ Just $ fromChannelD chan
176 step gst (Just (D.UnStream step1 st)) = do
177 r <- step1 gst st
178 return $ case r of
179 D.Yield a s -> D.Yield a (Just $ D.Stream step1 s)
180 D.Skip s -> D.Skip (Just $ D.Stream step1 s)
181 D.Stop -> D.Stop
182-}
184-- | Evaluate a stream asynchronously. In a serial stream, each element of the
185-- stream is generated as it is demanded by the consumer. `parEval` evaluates
186-- multiple elements of the stream ahead of time and serves the results from a
187-- buffer.
188--
189-- Note that the evaluation requires only one thread as only one stream needs
190-- to be evaluated. Therefore, the concurrency options that are relevant to
191-- multiple streams won't apply here e.g. maxThreads, eager, interleaved,
192-- ordered, stopWhen options won't have any effect.
193--
194{-# INLINE parEval #-}
195parEval :: MonadAsync m => (Config -> Config) -> Stream m a -> Stream m a
196parEval modifier input = withChannel modifier input (const id)
197 -- Stream.fromStreamD $ parEvalD cfg $ Stream.toStreamD stream
199-- | Definition:
200--
201-- >>> eval = Stream.parEval id
202--
203{-# INLINE eval #-}
204eval :: MonadAsync m => Stream m a -> Stream m a
205eval = parEval id
207-------------------------------------------------------------------------------
208-- combining two streams
209-------------------------------------------------------------------------------
211{-# INLINE _appendGeneric #-}
212_appendGeneric :: MonadAsync m =>
213 ((Config -> Config) -> m (Channel m a))
214 -> (Config -> Config)
215 -> K.Stream m a
216 -> K.Stream m a
217 -> K.Stream m a
218_appendGeneric newChan modifier stream1 stream2 = K.concatEffect action
220 where
222 action = do
223 chan <- newChan modifier
224 let cfg = modifier defaultConfig
225 done = K.nilM (stopChannel chan)
226 case getStopWhen cfg of
227 AllStop -> do
228 toChannelK chan stream2
229 toChannelK chan stream1
230 FirstStops -> do
231 toChannelK chan stream2
232 toChannelK chan (K.serial stream1 done)
233 AnyStops -> do
234 toChannelK chan (K.serial stream2 done)
235 toChannelK chan (K.serial stream1 done)
236 return $ Stream.toStreamK $ fromChannel chan
238-- | Create a new channel and add both the streams to it for async evaluation.
239-- The output stream is the result of the evaluation.
240{-# INLINE appendWithK #-}
241appendWithK :: MonadAsync m =>
242 (Config -> Config) -> K.Stream m a -> K.Stream m a -> K.Stream m a
243appendWithK modifier stream1 stream2 =
244{-
245 if getOrdered (modifier defaultConfig)
246 then parConcatMapK modifier id (stream1 `K.cons` K.fromPure stream2)
247 else _appendGeneric Append.newChannel modifier stream1 stream2
248-}
249 parConcatMapK modifier id (stream1 `K.cons` K.fromPure stream2)
251-- | Evaluate the first stream in the current thread and add the second stream
252-- to the supplied channel. This is to be used by a worker thread.
253--
254-- This can be used with parConcatMap:
255--
256-- @
257-- concatMap = K.parConcatMap (_appendWithChanK chan) f stream
258-- @
259--
260{-# INLINE _appendWithChanK #-}
261_appendWithChanK :: MonadAsync m =>
262 Channel m a -> K.Stream m a -> K.Stream m a -> K.Stream m a
263_appendWithChanK chan stream1 stream2 =
264 K.before (toChannelK chan stream2) stream1
267-- | Like 'append' but with a Config modifier.
268{-# INLINE combineWith #-}
269combineWith :: MonadAsync m =>
270 (Config -> Config) -> Stream m a -> Stream m a -> Stream m a
271combineWith modifier stream1 stream2 =
272 Stream.fromStreamK
273 $ appendWithK
274 modifier (Stream.toStreamK stream1) (Stream.toStreamK stream2)
276-- | Binary operation to evaluate two streams concurrently prioritizing the
277-- left stream.
278--
279-- If you want to combine more than two streams you almost always want the
280-- 'concat' operation instead. The performance of this operation degrades
281-- rapidly when more streams are combined as each operation adds one more
282-- concurrent channel. On the other hand, 'concat' uses a single channel for
283-- all streams. However, with this operation you can precisely control the
284-- scheduling by creating arbitrary shape expression trees.
285--
286-- >>> append2 = Stream.combineWith id
287--
288-- The following code finishes in 4 seconds:
289--
290-- >>> stream1 = Stream.fromEffect (delay 4)
291-- >>> stream2 = Stream.fromEffect (delay 2)
292-- >>> Stream.fold Fold.toList $ stream1 `Stream.append2` stream2
293-- 2 sec
294-- 4 sec
295-- [2,4]
296--
297{-# INLINE append2 #-}
298append2 :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
299append2 = combineWith id
301-- | Like 'append' but interleaves the streams fairly instead of prioritizing
302-- the left stream. This schedules all streams in a round robin fashion over
303-- limited number of threads.
304--
305-- >>> interleave2 = Stream.combineWith (Stream.interleaved True)
306--
307{-# INLINE interleave2 #-}
308interleave2 :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
309interleave2 = combineWith (interleaved True)
311-- | Like 'append' but with 'ordered' on.
312--
313-- >>> ahead2 = Stream.combineWith (Stream.ordered True)
314--
315{-# INLINE ahead2 #-}
316ahead2 :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
317ahead2 = combineWith (ordered True)
319-- | Like 'append2' but with 'eager' on.
320--
321-- >>> parallel2 = Stream.combineWith (Stream.eager True)
322--
323{-# INLINE parallel2 #-}
324parallel2 :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
325parallel2 = combineWith (eager True)
327-- | Like 'parallel2' but stops the output as soon as the first stream stops.
328--
329-- >>> parallelFst2 = Stream.combineWith (Stream.eager True . Stream.stopWhen Stream.FirstStops)
330--
331-- /Pre-release/
332{-# INLINE parallelFst2 #-}
333parallelFst2 :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
334parallelFst2 = combineWith (eager True . stopWhen FirstStops)
336-- | Like 'parallel2' but stops the output as soon as any of the two streams
337-- stops.
338--
339-- >>> parallelMin2 = Stream.combineWith (Stream.eager True . Stream.stopWhen Stream.AnyStops)
340--
341-- /Pre-release/
342{-# INLINE parallelMin2 #-}
343parallelMin2 :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
344parallelMin2 = combineWith (eager True . stopWhen AnyStops)
346-------------------------------------------------------------------------------
347-- concat streams
348-------------------------------------------------------------------------------
350-- | A runner function takes a queuing function @q@ and a stream, it splits the
351-- input stream, queuing the tail and using the head to generate a stream.
352-- 'mkEnqueue' takes a runner function and generates the queuing function @q@.
353-- Note that @q@ and the runner are mutually recursive, mkEnqueue ties the knot
354-- between the two.
355{-# INLINE mkEnqueue #-}
356mkEnqueue :: MonadAsync m =>
357 Channel m b
358 -> ((K.Stream m a -> m ()) -> K.Stream m a -> K.Stream m b)
359 -> m (K.Stream m a -> m ())
360mkEnqueue chan runner = do
361 runInIO <- askRunInIO
362 return
363 $ let q stream = do
364 -- Enqueue the outer loop
365 liftIO $ enqueue chan False (runInIO, runner q stream)
366 -- XXX In case of eager dispatch we can just directly dispatch
367 -- a worker with the tail stream here rather than first queuing
368 -- and then dispatching a worker which dequeues the work. The
369 -- older implementation did a direct dispatch here and its perf
370 -- characterstics looked much better.
371 eagerDispatch chan
372 in q
374-- | Takes the head element of the input stream and queues the tail of the
375-- stream to the channel, then maps the supplied function on the head and
376-- evaluates the resulting stream.
377--
378-- This function is designed to be used by worker threads on a channel to
379-- concurrently map and evaluate a stream.
380{-# INLINE parConcatMapChanK #-}
381parConcatMapChanK :: MonadAsync m =>
382 Channel m b -> (a -> K.Stream m b) -> K.Stream m a -> K.Stream m b
383parConcatMapChanK chan f stream =
384 let run q = concatMapDivK q f
385 in K.concatMapEffect (`run` stream) (mkEnqueue chan run)
386 -- K.parConcatMap (_appendWithChanK chan) f stream
388{-# INLINE parConcatMapChanKAny #-}
389parConcatMapChanKAny :: MonadAsync m =>
390 Channel m b -> (a -> K.Stream m b) -> K.Stream m a -> K.Stream m b
391parConcatMapChanKAny chan f stream =
392 let done = K.nilM (stopChannel chan)
393 run q = concatMapDivK q (\x -> K.serial (f x) done)
394 in K.concatMapEffect (`run` stream) (mkEnqueue chan run)
396{-# INLINE parConcatMapChanKFirst #-}
397parConcatMapChanKFirst :: MonadAsync m =>
398 Channel m b -> (a -> K.Stream m b) -> K.Stream m a -> K.Stream m b
399parConcatMapChanKFirst chan f stream =
400 let done = K.nilM (stopChannel chan)
401 run q = concatMapDivK q f
402 in K.concatEffect $ do
403 res <- K.uncons stream
404 case res of
405 Nothing -> return K.nil
406 Just (h, t) -> do
407 q <- mkEnqueue chan run
408 q t
409 return $ K.serial (f h) done
411{-# INLINE parConcatMapChanKGeneric #-}
412parConcatMapChanKGeneric :: MonadAsync m =>
413 (Config -> Config)
414 -> Channel m b
415 -> (a -> K.Stream m b)
416 -> K.Stream m a
417 -> K.Stream m b
418parConcatMapChanKGeneric modifier chan f stream = do
419 let cfg = modifier defaultConfig
420 case getStopWhen cfg of
421 AllStop -> parConcatMapChanK chan f stream
422 FirstStops -> parConcatMapChanKFirst chan f stream
423 AnyStops -> parConcatMapChanKAny chan f stream
425-- XXX Add a deep evaluation variant that evaluates individual elements in the
426-- generated streams in parallel.
428-- | Allocate a channel and use it to concurrently evaluate the streams
429-- generated by the mapped function.
430--
431{-# INLINE parConcatMapK #-}
432parConcatMapK :: MonadAsync m =>
433 (Config -> Config) -> (a -> K.Stream m b) -> K.Stream m a -> K.Stream m b
434parConcatMapK modifier f input =
435 let g = parConcatMapChanKGeneric modifier
436 in withChannelK modifier input (`g` f)
438-- | Map each element of the input to a stream and then concurrently evaluate
439-- and concatenate the resulting streams. Multiple streams may be evaluated
440-- concurrently but earlier streams are perferred. Output from the streams are
441-- used as they arrive.
442--
443-- Definition:
444--
445-- >>> parConcatMap modifier f stream = Stream.parConcat modifier $ fmap f stream
446--
447-- Examples:
448--
449-- >>> f cfg xs = Stream.fold Fold.toList $ Stream.parConcatMap cfg id $ Stream.fromList xs
450--
451-- The following streams finish in 4 seconds:
452--
453-- >>> stream1 = Stream.fromEffect (delay 4)
454-- >>> stream2 = Stream.fromEffect (delay 2)
455-- >>> stream3 = Stream.fromEffect (delay 1)
456-- >>> f id [stream1, stream2, stream3]
457-- 1 sec
458-- 2 sec
459-- 4 sec
460-- [1,2,4]
461--
462-- Limiting threads to 2 schedules the third stream only after one of the first
463-- two has finished, releasing a thread:
464--
465-- >>> f (Stream.maxThreads 2) [stream1, stream2, stream3]
466-- ...
467-- [2,1,4]
468--
469-- When used with a Single thread it behaves like serial concatMap:
470--
471-- >>> f (Stream.maxThreads 1) [stream1, stream2, stream3]
472-- ...
473-- [4,2,1]
474--
475-- >>> stream1 = Stream.fromList [1,2,3]
476-- >>> stream2 = Stream.fromList [4,5,6]
477-- >>> f (Stream.maxThreads 1) [stream1, stream2]
478-- [1,2,3,4,5,6]
479--
480{-# INLINE parConcatMap #-}
481parConcatMap :: MonadAsync m =>
482 (Config -> Config) -> (a -> Stream m b) -> Stream m a -> Stream m b
483parConcatMap modifier f stream =
484 Stream.fromStreamK
485 $ parConcatMapK
486 modifier (Stream.toStreamK . f) (Stream.toStreamK stream)
488-- | Definition:
489--
490-- >>> concatMap = Stream.parConcatMap id
491--
492{-# INLINE concatMap #-}
493concatMap :: MonadAsync m => (a -> Stream m b) -> Stream m a -> Stream m b
494concatMap = parConcatMap id
496-- | Map each element of the input to a stream and then concurrently evaluate
497-- and interleave the resulting streams. Unlike 'concatMap' which prefers to
498-- evaluate the earlier stream first, this schedules all streams in a round
499-- robin fashion over the available threads.
500--
501-- >>> concatMapInterleave = Stream.parConcatMap (Stream.interleaved True)
502--
503-- When used with a single thread it behaves like serial interleaving:
504--
505-- >>> f cfg xs = Stream.fold Fold.toList $ Stream.parConcatMap (Stream.interleaved True . cfg) id $ Stream.fromList xs
506--
507-- >>> stream1 = Stream.fromList [1,2,3]
508-- >>> stream2 = Stream.fromList [4,5,6]
509-- >>> f (Stream.maxThreads 1) [stream1, stream2]
510-- [1,4,2,5,3,6]
511--
512-- /Works only on finite number of streams/
513--
514{-# INLINE concatMapInterleave #-}
515concatMapInterleave :: MonadAsync m => (a -> Stream m b) -> Stream m a -> Stream m b
516concatMapInterleave = parConcatMap (interleaved True)
518-- | Evaluate the streams in the input stream concurrently and combine them.
519--
520-- >>> parConcat modifier = Stream.parConcatMap modifier id
521--
522{-# INLINE parConcat #-}
523parConcat :: MonadAsync m =>
524 (Config -> Config) -> Stream m (Stream m a) -> Stream m a
525parConcat modifier = parConcatMap modifier id
527-- | Definition:
528--
529-- >>> concat = Stream.parConcat id
530--
531{-# INLINE concat #-}
532concat :: MonadAsync m => Stream m (Stream m a) -> Stream m a
533concat = parConcat id
535-------------------------------------------------------------------------------
536-- concat Lists
537-------------------------------------------------------------------------------
539-- | Like 'parConcat' but works on a list of streams.
540--
541-- >>> parConcatList modifier = Stream.parConcat modifier . Stream.fromList
542--
543{-# INLINE parConcatList #-}
544parConcatList :: MonadAsync m => (Config -> Config) -> [Stream m a] -> Stream m a
545parConcatList modifier = parConcat modifier . Stream.fromList
547-- | Like 'concat' but works on a list of streams.
548--
549-- >>> parLazy = Stream.parConcatList id
550--
551{-# INLINE parLazy #-}
552parLazy :: MonadAsync m => [Stream m a] -> Stream m a
553parLazy = parConcatList id
555-- | Like 'parLazy' but interleaves the streams fairly instead of prioritizing
556-- the left stream. This schedules all streams in a round robin fashion over
557-- limited number of threads.
558--
559-- >>> parLazyInterleaved = Stream.parConcatList (Stream.interleaved True)
560--
561{-# INLINE parLazyInterleaved #-}
562parLazyInterleaved :: MonadAsync m => [Stream m a] -> Stream m a
563parLazyInterleaved = parConcatList (interleaved True)
565-- | Like 'parLazy' but with 'ordered' on.
566--
567-- >>> parLazyOrdered = Stream.parConcatList (Stream.ordered True)
568--
569{-# INLINE parLazyOrdered #-}
570parLazyOrdered :: MonadAsync m => [Stream m a] -> Stream m a
571parLazyOrdered = parConcatList (ordered True)
573-- | Like 'parLazy' but with 'eager' on.
574--
575-- >>> parEager = Stream.parConcatList (Stream.eager True)
576--
577{-# INLINE parEager #-}
578parEager :: MonadAsync m => [Stream m a] -> Stream m a
579parEager = parConcatList (eager True)
581-- | Like 'parEager' but stops the output as soon as the first stream stops.
582--
583-- >>> parEagerFst = Stream.parConcatList (Stream.eager True . Stream.stopWhen Stream.FirstStops)
584--
585{-# INLINE parEagerFst #-}
586parEagerFst :: MonadAsync m => [Stream m a] -> Stream m a
587parEagerFst = parConcatList (eager True . stopWhen FirstStops)
589-- | Like 'parEager' but stops the output as soon as any of the two streams
590-- stops.
591--
592-- Definition:
593--
594-- >>> parEagerMin = Stream.parConcatList (Stream.eager True . Stream.stopWhen Stream.AnyStops)
595--
596{-# INLINE parEagerMin #-}
597parEagerMin :: MonadAsync m => [Stream m a] -> Stream m a
598parEagerMin = parConcatList (eager True . stopWhen AnyStops)
600-------------------------------------------------------------------------------
601-- Applicative
602-------------------------------------------------------------------------------
604-- | Apply an argument stream to a function stream concurrently. Uses a
605-- shared channel for all individual applications within a stream application.
606{-# INLINE parApply #-}
607{-# SPECIALIZE parApply ::
608 (Config -> Config) -> Stream IO (a -> b) -> Stream IO a -> Stream IO b #-}
609parApply :: MonadAsync m =>
610 (Config -> Config) -> Stream m (a -> b) -> Stream m a -> Stream m b
611parApply modifier stream1 stream2 =
612 parConcatMap
613 modifier
614 (\g -> parConcatMap modifier (Stream.fromPure . g) stream2)
615 stream1
617{-# INLINE apply #-}
618{-# SPECIALIZE apply :: Stream IO (a -> b) -> Stream IO a -> Stream IO b #-}
619apply :: MonadAsync m => Stream m (a -> b) -> Stream m a -> Stream m b
620apply = parApply id
622-------------------------------------------------------------------------------
623-- Map
624-------------------------------------------------------------------------------
626-- |
627-- Definition:
628--
629-- >>> parMapM modifier f = Stream.parConcatMap modifier (Stream.fromEffect . f)
630--
631-- Example, the following example finishes in 1 second as all actions run in
632-- parallel. Even though results are available out of order they are ordered
633-- due to the config option::
634--
635-- >>> f x = delay x >> return x
636-- >>> Stream.fold Fold.toList $ Stream.parMapM (Stream.ordered True) f $ Stream.fromList [3,2,1]
637-- 1 sec
638-- 2 sec
639-- 3 sec
640-- [3,2,1]
641--
642{-# INLINE parMapM #-}
643parMapM :: MonadAsync m =>
644 (Config -> Config) -> (a -> m b) -> Stream m a -> Stream m b
645parMapM modifier f = parConcatMap modifier (Stream.fromEffect . f)
647-- |
648-- Definition:
649--
650-- >>> mapM = Stream.parMapM id
651--
652{-# INLINE mapM #-}
653mapM :: MonadAsync m => (a -> m b) -> Stream m a -> Stream m b
654mapM = parMapM id
656-- |
657-- >>> parSequence modifier = Stream.parMapM modifier id
658--
659{-# INLINE parSequence #-}
660parSequence :: MonadAsync m =>
661 (Config -> Config) -> Stream m (m a) -> Stream m a
662parSequence modifier = parMapM modifier id
664-- |
665-- >>> sequence = Stream.parSequence id
666--
667{-# INLINE sequence #-}
668sequence :: MonadAsync m =>
669 Stream m (m a) -> Stream m a
670sequence = parSequence id
672-- | Evaluates the streams being zipped in separate threads than the consumer.
673-- The zip function is evaluated in the consumer thread.
674--
675-- >>> parZipWithM cfg f m1 m2 = Stream.zipWithM f (Stream.parEval cfg m1) (Stream.parEval cfg m2)
676--
677-- Multi-stream concurrency options won't apply here, see the notes in
678-- 'parEval'.
679--
680-- If you want to evaluate the zip function as well in a separate thread, you
681-- can use a 'parEval' on 'parZipWithM'.
682--
683{-# INLINE parZipWithM #-}
684parZipWithM :: MonadAsync m
685 => (Config -> Config)
686 -> (a -> b -> m c)
687 -> Stream m a
688 -> Stream m b
689 -> Stream m c
690parZipWithM cfg f m1 m2 = Stream.zipWithM f (parEval cfg m1) (parEval cfg m2)
692-- |
693-- >>> parZipWith cfg f = Stream.parZipWithM cfg (\a b -> return $ f a b)
694--
695-- >>> m1 = Stream.fromList [1,2,3]
696-- >>> m2 = Stream.fromList [4,5,6]
697-- >>> Stream.fold Fold.toList $ Stream.parZipWith id (,) m1 m2
698-- [(1,4),(2,5),(3,6)]
699--
700{-# INLINE parZipWith #-}
701parZipWith :: MonadAsync m
702 => (Config -> Config)
703 -> (a -> b -> c)
704 -> Stream m a
705 -> Stream m b
706 -> Stream m c
707parZipWith cfg f = parZipWithM cfg (\a b -> return $ f a b)
709-------------------------------------------------------------------------------
710-- concatIterate
711-------------------------------------------------------------------------------
713-- | Same as 'concatIterate' but concurrent.
714--
715-- /Pre-release/
716{-# INLINE parConcatIterate #-}
717parConcatIterate :: MonadAsync m =>
718 (Config -> Config)
719 -> (a -> Stream m a)
720 -> Stream m a
721 -> Stream m a
722parConcatIterate modifier f input =
723 Stream.fromStreamK
724 $ withChannelK modifier (Stream.toStreamK input) iterateStream
726 where
728 iterateStream channel stream =
729 parConcatMapChanKGeneric modifier channel (generate channel) stream
731 generate channel x =
732 -- XXX The channel q should be FIFO for DFS, otherwise it is BFS
733 x `K.cons` iterateStream channel (Stream.toStreamK $ f x)
735-------------------------------------------------------------------------------
736-- Generate
737-------------------------------------------------------------------------------
739-- |
740-- Definition:
741--
742-- >>> parRepeatM cfg = Stream.parSequence cfg . Stream.repeat
743--
744-- Generate a stream by repeatedly executing a monadic action forever.
745{-# INLINE parRepeatM #-}
746parRepeatM :: MonadAsync m => (Config -> Config) -> m a -> Stream m a
747parRepeatM cfg = parSequence cfg . Stream.repeat
749-- |
750-- Definition:
751--
752-- >>> repeatM = Stream.parRepeatM id
753--
754{-# INLINE repeatM #-}
755repeatM :: MonadAsync m => m a -> Stream m a
756repeatM = parRepeatM id
758-- | Generate a stream by concurrently performing a monadic action @n@ times.
759--
760-- Definition:
761--
762-- >>> parReplicateM cfg n = Stream.parSequence cfg . Stream.replicate n
763--
764-- Example, 'parReplicateM' in the following example executes all the
765-- replicated actions concurrently, thus taking only 1 second:
766--
767-- >>> Stream.fold Fold.drain $ Stream.parReplicateM id 10 $ delay 1
768-- ...
769--
770{-# INLINE parReplicateM #-}
771parReplicateM :: MonadAsync m => (Config -> Config) -> Int -> m a -> Stream m a
772parReplicateM cfg n = parSequence cfg . Stream.replicate n
774-- |
775-- Definition:
776--
777-- >>> replicateM n = Stream.parReplicateM id
778--
779{-# INLINE replicateM #-}
780replicateM :: MonadAsync m => Int -> m a -> Stream m a
781replicateM = parReplicateM id
783-------------------------------------------------------------------------------
784-- Reactive
785-------------------------------------------------------------------------------
787-- Note: we can use another API with two callbacks stop and yield if we want
788-- the callback to be able to indicate end of stream.
789--
790-- | Generates a callback and a stream pair. The callback returned is used to
791-- queue values to the stream. The stream is infinite, there is no way for the
792-- callback to indicate that it is done now.
793--
794-- /Pre-release/
795--
796{-# INLINE_NORMAL newCallbackStream #-}
797newCallbackStream :: MonadAsync m => m (a -> m (), Stream m a)
798newCallbackStream = do
799 chan <- newChannel (eager True)
801 -- XXX Add our own thread-id to the SVar as we can not know the callback's
802 -- thread-id and the callback is not run in a managed worker. We need to
803 -- handle this better.
804 liftIO myThreadId
805 >>= modifyThread (workerThreads chan) (outputDoorBell chan)
807 let callback a =
808 liftIO
809 $ void
810 $ sendWithDoorBell
811 (outputQueue chan) (outputDoorBell chan) (ChildYield a)
812 -- XXX Use fromChannelD?
813 return (callback, fromChannel chan)
815-- | Supplies a stream generating callback to a callback setter function. Each
816-- invocation of the callback results in a value being generated in the
817-- resulting stream.
818--
819-- /Pre-release/
820--
821{-# INLINE fromCallback #-}
822fromCallback :: MonadAsync m => ((a -> m ()) -> m ()) -> Stream m a
823fromCallback setCallback = Stream.concatEffect $ do
824 (callback, stream) <- newCallbackStream
825 setCallback callback
826 return stream
828{-# INLINE_NORMAL tapCountD #-}
829tapCountD
830 :: MonadAsync m
831 => (a -> Bool)
832 -> (D.Stream m Int -> m b)
833 -> D.Stream m a
834 -> D.Stream m a
835tapCountD predicate fld (D.Stream step state) = D.Stream step' Nothing
836 where
838 {-# INLINE_LATE step' #-}
839 step' _ Nothing = do
840 -- As long as we are using an "Int" for counts lockfree reads from
841 -- Var should work correctly on both 32-bit and 64-bit machines.
842 -- However, an Int on a 32-bit machine may overflow quickly.
843 countVar <- liftIO $ Unboxed.newIORef (0 :: Int)
844 tid <- forkManaged
845 $ void $ fld
846 $ Unboxed.toStreamD countVar
847 return $ Skip (Just (countVar, tid, state))
849 step' gst (Just (countVar, tid, st)) = do
850 r <- step gst st
851 case r of
852 Yield x s -> do
853 when (predicate x)
854 $ liftIO $ Unboxed.modifyIORef' countVar (+ 1)
855 return $ Yield x (Just (countVar, tid, s))
856 Skip s -> return $ Skip (Just (countVar, tid, s))
857 Stop -> do
858 liftIO $ killThread tid
859 return Stop
861-- | @tapCount predicate fold stream@ taps the count of those elements in the
862-- stream that pass the @predicate@. The resulting count stream is sent to
863-- another thread which folds it using @fold@.
864--
865-- For example, to print the count of elements processed every second:
866--
867-- >>> rate = Stream.rollingMap2 (flip (-)) . Stream.delayPost 1
868-- >>> report = Stream.fold (Fold.drainMapM print) . rate
869-- >>> tap = Stream.tapCount (const True) report
870-- >>> go = Stream.fold Fold.drain $ tap $ Stream.enumerateFrom 0
871--
872-- Note: This may not work correctly on 32-bit machines because of Int
873-- overflow.
874--
875-- /Pre-release/
876--
877{-# INLINE tapCount #-}
878tapCount ::
879 (MonadAsync m)
880 => (a -> Bool)
881 -> (Stream m Int -> m b)
882 -> Stream m a
883 -> Stream m a
884tapCount predicate f xs =
885 Stream.fromStreamD
886 $ tapCountD predicate (f . Stream.fromStreamD)
887 $ Stream.toStreamD xs