on master help sign in
Import from as low level modules as possible
b10f17cHarendra Kumar
src/Streamly/Internal/Data/Fold.hs+2−56
@@ -67,7 +67,6 @@ module Streamly.Internal.Data.Fold
67 , toList
67 , toList
68 , toListRevF -- experimental
68 , toListRevF -- experimental
69 -- $toListRevF
69 -- $toListRevF
70− , toParallelSVar
71
70
72 -- * Terminating Folds
71 -- * Terminating Folds
73 , drainN
72 , drainN
@@ -89,7 +88,6 @@ module Streamly.Internal.Data.Fold
89 , and
88 , and
90 , or
89 , or
91 -- , the
90 -- , the
92− , toParallelSVarLimited
93
91
94 -- * Adapting
92 -- * Adapting
95 , hoist
93 , hoist
@@ -226,7 +224,6 @@ module Streamly.Internal.Data.Fold
226where
224where
227
225
228import Control.Monad (void, join)
226import Control.Monad (void, join)
229−import Control.Monad.IO.Class (MonadIO(..))
230import Data.Bifunctor (first)
227import Data.Bifunctor (first)
231import Data.Functor.Identity (Identity(..))
228import Data.Functor.Identity (Identity(..))
232import Data.Int (Int64)
229import Data.Int (Int64)
@@ -235,15 +232,13 @@ import Data.Maybe (isJust, fromJust)
235#if __GLASGOW_HASKELL__ < 804
232#if __GLASGOW_HASKELL__ < 804
236import Data.Semigroup (Semigroup((<>)))
233import Data.Semigroup (Semigroup((<>)))
237#endif
234#endif
238−import Streamly.Internal.Data.Either.Strict (Either'(..))
235+import Streamly.Internal.Data.Either.Strict
236+ (Either'(..), fromLeft', fromRight', isLeft', isRight')
239import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
237import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
240import Streamly.Internal.Data.Pipe.Types (Pipe (..), PipeState(..))
238import Streamly.Internal.Data.Pipe.Types (Pipe (..), PipeState(..))
241import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))
239import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))
242−import Streamly.Internal.Data.Either.Strict
243− (fromLeft', fromRight', isLeft', isRight')
244
240
245import qualified Data.Map.Strict as Map
241import qualified Data.Map.Strict as Map
246−import qualified Streamly.Internal.Data.Fold.Types as FL
247import qualified Streamly.Internal.Data.Pipe.Types as Pipe
242import qualified Streamly.Internal.Data.Pipe.Types as Pipe
248import qualified Prelude
243import qualified Prelude
249
244
@@ -254,7 +249,6 @@ import Prelude hiding
254 , reverse, iterate, init, and, or, lookup, (!!)
249 , reverse, iterate, init, and, or, lookup, (!!)
255 , scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip
250 , scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip
256 , span, splitAt, break, mapM)
251 , span, splitAt, break, mapM)
257−import Streamly.Internal.Data.SVar
258import Streamly.Internal.Data.Fold.Types
252import Streamly.Internal.Data.Fold.Types
259
253
260------------------------------------------------------------------------------
254------------------------------------------------------------------------------
@@ -2163,51 +2157,3 @@ concatSequence _f _p = undefined
2163chunksBetween :: -- Monad m =>
2157chunksBetween :: -- Monad m =>
2164 Int -> Int -> Fold m a b -> Fold m b c -> Fold m a c
2158 Int -> Int -> Fold m a b -> Fold m b c -> Fold m a c
2165chunksBetween _low _high _f1 _f2 = undefined
2159chunksBetween _low _high _f1 _f2 = undefined
2166−
2167−------------------------------------------------------------------------------
2168−-- Fold to a Parallel SVar
2169−------------------------------------------------------------------------------
2170−
2171−{-# INLINE toParallelSVar #-}
2172−toParallelSVar :: MonadIO m => SVar t m a -> Maybe WorkerInfo -> Fold m a ()
2173−toParallelSVar svar winfo = Fold step initial extract
2174−
2175− where
2176−
2177− initial = return $ Partial ()
2178−
2179− -- XXX we can have a separate fold for unlimited buffer case to avoid a
2180− -- branch in the step here.
2181− step () x =
2182− liftIO $ do
2183− decrementBufferLimit svar
2184− void $ send svar (ChildYield x)
2185− return $ FL.Partial ()
2186−
2187− extract () = liftIO $ sendStop svar winfo
2188−
2189−{-# INLINE toParallelSVarLimited #-}
2190−toParallelSVarLimited :: MonadIO m
2191− => SVar t m a -> Maybe WorkerInfo -> Fold m a ()
2192−toParallelSVarLimited svar winfo = Fold step initial extract
2193−
2194− where
2195−
2196− initial = return $ Partial True
2197−
2198− step True x =
2199− liftIO $ do
2200− yieldLimitOk <- decrementYieldLimit svar
2201− if yieldLimitOk
2202− then do
2203− decrementBufferLimit svar
2204− void $ send svar (ChildYield x)
2205− return $ FL.Partial True
2206− else do
2207− cleanupSVarFromWorker svar
2208− sendStop svar winfo
2209− return $ FL.Done ()
2210− step False _ = return $ FL.Done ()
2211−
2212− extract True = liftIO $ sendStop svar winfo
2213− extract False = return ()
src/Streamly/Internal/Data/IORef/Prim.hs+28−2
@@ -1,7 +1,5 @@
1{-# LANGUAGE UnboxedTuples #-}
1{-# LANGUAGE UnboxedTuples #-}
2
2
3−#include "inline.hs"
4−
5-- |
3-- |
6-- Module : Streamly.Internal.Data.IORef.Prim
4-- Module : Streamly.Internal.Data.IORef.Prim
7-- Copyright : (c) 2019 Composewell Technologies
5-- Copyright : (c) 2019 Composewell Technologies
@@ -37,14 +35,22 @@ module Streamly.Internal.Data.IORef.Prim
37
35
38 -- * Read
36 -- * Read
39 , readIORef
37 , readIORef
38+ , toStream
39+ , toStreamD
40 )
40 )
41where
41where
42
42
43+#include "inline.hs"
44+
45+import Control.Monad.IO.Class (MonadIO(..))
43import Control.Monad.Primitive (primitive_)
46import Control.Monad.Primitive (primitive_)
44import Data.Primitive.Types (Prim, sizeOf#, readByteArray#, writeByteArray#)
47import Data.Primitive.Types (Prim, sizeOf#, readByteArray#, writeByteArray#)
45import GHC.Exts (MutableByteArray#, newByteArray#, RealWorld)
48import GHC.Exts (MutableByteArray#, newByteArray#, RealWorld)
46import GHC.IO (IO(..))
49import GHC.IO (IO(..))
47
50
51+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
52+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
53+
48-- | An 'IORef' holds a single 'Prim' value.
54-- | An 'IORef' holds a single 'Prim' value.
49data IORef a = IORef (MutableByteArray# RealWorld)
55data IORef a = IORef (MutableByteArray# RealWorld)
50
56
@@ -82,3 +88,23 @@ modifyIORef' :: Prim a => IORef a -> (a -> a) -> IO ()
82modifyIORef' (IORef arr#) g = primitive_ $ \s# ->
88modifyIORef' (IORef arr#) g = primitive_ $ \s# ->
83 case readByteArray# arr# 0# s# of
89 case readByteArray# arr# 0# s# of
84 (# s'#, a #) -> let a' = g a in a' `seq` writeByteArray# arr# 0# a' s'#
90 (# s'#, a #) -> let a' = g a in a' `seq` writeByteArray# arr# 0# a' s'#
91+
92+-- | Generate a stream by continuously reading the IORef.
93+--
94+-- /Internal/
95+{-# INLINE_NORMAL toStreamD #-}
96+toStreamD :: (MonadIO m, Prim a) => IORef a -> D.Stream m a
97+toStreamD var = D.Stream step ()
98+
99+ where
100+
101+ {-# INLINE_LATE step #-}
102+ step _ () = liftIO (readIORef var) >>= \x -> return $ D.Yield x ()
103+
104+-- | Construct a stream by reading a 'Prim' 'IORef' repeatedly.
105+--
106+-- /Internal/
107+--
108+{-# INLINE toStream #-}
109+toStream :: (K.IsStream t, MonadIO m, Prim a) => IORef a -> t m a
110+toStream = D.fromStreamD . toStreamD
src/Streamly/Internal/Data/Stream/Ahead.hs+5−3
@@ -42,10 +42,12 @@ import qualified Data.Heap as H
42
42
43import Streamly.Internal.Data.Stream.SVar (fromSVar)
43import Streamly.Internal.Data.Stream.SVar (fromSVar)
44import Streamly.Internal.Data.SVar
44import Streamly.Internal.Data.SVar
45−import Streamly.Internal.Data.Stream.StreamK
45+import Streamly.Internal.Data.Stream.StreamK.Type
46 (IsStream(..), Stream, mkStream, foldStream, foldStreamShared)
46 (IsStream(..), Stream, mkStream, foldStream, foldStreamShared)
47+
47−import qualified Streamly.Internal.Data.Stream.StreamK as K
48+import qualified Streamly.Internal.Data.Stream.StreamK as K (withLocal)
49+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
48−import qualified Streamly.Internal.Data.Stream.StreamD as D
50+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
49
51
50import Prelude hiding (map)
52import Prelude hiding (map)
51
53
src/Streamly/Internal/Data/Stream/Async.hs+7−5
@@ -50,12 +50,14 @@ import Prelude hiding (map)
50import qualified Data.Set as S
50import qualified Data.Set as S
51
51
52import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS)
52import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS)
53−import Streamly.Internal.Data.Stream.SVar (fromSVar)
53+import Streamly.Internal.Data.Stream.SVar (fromSVar, fromSVarD)
54import Streamly.Internal.Data.SVar
54import Streamly.Internal.Data.SVar
55−import Streamly.Internal.Data.Stream.StreamK
55+import Streamly.Internal.Data.Stream.StreamK.Type
56 (IsStream(..), Stream, mkStream, foldStream, adapt, foldStreamShared)
56 (IsStream(..), Stream, mkStream, foldStream, adapt, foldStreamShared)
57+
57−import qualified Streamly.Internal.Data.Stream.StreamK as K
58+import qualified Streamly.Internal.Data.Stream.StreamK as K (withLocal)
59+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
58−import qualified Streamly.Internal.Data.Stream.StreamD as D
60+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
59
61
60#include "Instances.hs"
62#include "Instances.hs"
61
63
@@ -504,7 +506,7 @@ mkAsyncD m = D.Stream step Nothing
504
506
505 step gst Nothing = do
507 step gst Nothing = do
506 sv <- newAsyncVar gst (D.fromStreamD m)
508 sv <- newAsyncVar gst (D.fromStreamD m)
507− return $ D.Skip $ Just $ D.fromSVar sv
509+ return $ D.Skip $ Just $ fromSVarD sv
508
510
509 step gst (Just (D.UnStream step1 st)) = do
511 step gst (Just (D.UnStream step1 st)) = do
510 r <- step1 gst st
512 r <- step1 gst st
src/Streamly/Internal/Data/Stream/IsStream/Combinators.hs+1−1
@@ -27,7 +27,7 @@ import Control.Monad.IO.Class (MonadIO(liftIO))
27import Data.Int (Int64)
27import Data.Int (Int64)
28
28
29import Streamly.Internal.Data.SVar
29import Streamly.Internal.Data.SVar
30−import Streamly.Internal.Data.Stream.StreamK
30+import Streamly.Internal.Data.Stream.StreamK.Type
31import Streamly.Internal.Data.Stream.Serial (SerialT)
31import Streamly.Internal.Data.Stream.Serial (SerialT)
32
32
33-------------------------------------------------------------------------------
33-------------------------------------------------------------------------------
src/Streamly/Internal/Data/Stream/IsStream/Common.hs+4−3
@@ -53,15 +53,16 @@ import Streamly.Internal.Data.Fold.Types (Fold (..))
53import Streamly.Internal.Data.Stream.IsStream.Combinators (maxYields)
53import Streamly.Internal.Data.Stream.IsStream.Combinators (maxYields)
54import Streamly.Internal.Data.Stream.Prelude (fromStreamS, toStreamS)
54import Streamly.Internal.Data.Stream.Prelude (fromStreamS, toStreamS)
55import Streamly.Internal.Data.Stream.Serial (SerialT)
55import Streamly.Internal.Data.Stream.Serial (SerialT)
56−import Streamly.Internal.Data.Stream.StreamD (fromStreamD, toStreamD)
56+import Streamly.Internal.Data.Stream.StreamD.Type (fromStreamD, toStreamD)
57−import Streamly.Internal.Data.Stream.StreamK (IsStream())
57+import Streamly.Internal.Data.Stream.StreamK.Type (IsStream())
58import Streamly.Internal.Data.SVar (MonadAsync)
58import Streamly.Internal.Data.SVar (MonadAsync)
59import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
59import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
60
60
61import qualified Streamly.Internal.Data.Stream.Parallel as Par
61import qualified Streamly.Internal.Data.Stream.Parallel as Par
62import qualified Streamly.Internal.Data.Stream.Prelude as P
62import qualified Streamly.Internal.Data.Stream.Prelude as P
63import qualified Streamly.Internal.Data.Stream.Serial as Serial
63import qualified Streamly.Internal.Data.Stream.Serial as Serial
64−import qualified Streamly.Internal.Data.Stream.StreamK as K
64+import qualified Streamly.Internal.Data.Stream.StreamK as K (repeatM)
65+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
65import qualified Streamly.Internal.Data.Stream.StreamD as D
66import qualified Streamly.Internal.Data.Stream.StreamD as D
66#ifdef USE_STREAMK_ONLY
67#ifdef USE_STREAMK_ONLY
67import qualified Streamly.Internal.Data.Stream.StreamK as S
68import qualified Streamly.Internal.Data.Stream.StreamK as S
src/Streamly/Internal/Data/Stream/IsStream/Eliminate.hs+2−1
@@ -164,6 +164,7 @@ import Streamly.Internal.Data.Stream.Serial (SerialT)
164
164
165import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as A
165import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as A
166import qualified Streamly.Internal.Data.Fold as FL
166import qualified Streamly.Internal.Data.Fold as FL
167+import qualified Streamly.Internal.Data.Stream.Parallel as Par
167import qualified Streamly.Internal.Data.Stream.Prelude as P
168import qualified Streamly.Internal.Data.Stream.Prelude as P
168import qualified Streamly.Internal.Data.Stream.StreamD as D
169import qualified Streamly.Internal.Data.Stream.StreamD as D
169import qualified Streamly.Internal.Data.Stream.StreamK as K
170import qualified Streamly.Internal.Data.Stream.StreamK as K
@@ -831,7 +832,7 @@ toPureRev = foldl' (flip K.cons) K.nil
831{-# INLINE (|$.) #-}
832{-# INLINE (|$.) #-}
832(|$.) :: (IsStream t, MonadAsync m) => (t m a -> m b) -> (t m a -> m b)
833(|$.) :: (IsStream t, MonadAsync m) => (t m a -> m b) -> (t m a -> m b)
833-- (|$.) f = f . Async.mkAsync
834-- (|$.) f = f . Async.mkAsync
834−(|$.) f = f . D.mkParallel
835+(|$.) f = f . Par.mkParallel
835
836
836infixr 0 |$.
837infixr 0 |$.
837
838
src/Streamly/Internal/Data/Stream/IsStream/Generate.hs+3−10
@@ -75,7 +75,6 @@ module Streamly.Internal.Data.Stream.IsStream.Generate
75 , fromListM
75 , fromListM
76 , K.fromFoldable
76 , K.fromFoldable
77 , fromFoldableM
77 , fromFoldableM
78− , fromPrimIORef
79 , fromCallback
78 , fromCallback
80
79
81 -- * Deprecated
80 -- * Deprecated
@@ -102,8 +101,8 @@ import Streamly.Internal.Data.Stream.StreamK (IsStream((|:), consM))
102import Streamly.Internal.Data.Stream.Serial (SerialT, WSerialT)
101import Streamly.Internal.Data.Stream.Serial (SerialT, WSerialT)
103import Streamly.Internal.Data.Stream.Zip (ZipSerialM)
102import Streamly.Internal.Data.Stream.Zip (ZipSerialM)
104import Streamly.Internal.Data.Time.Units (AbsTime , RelTime64, addToAbsTime64)
103import Streamly.Internal.Data.Time.Units (AbsTime , RelTime64, addToAbsTime64)
105−import Streamly.Internal.Data.IORef.Prim (Prim, IORef)
106
104
105+import qualified Streamly.Internal.Data.Stream.Parallel as Par
107import qualified Streamly.Internal.Data.Stream.Prelude as P
106import qualified Streamly.Internal.Data.Stream.Prelude as P
108import qualified Streamly.Internal.Data.Stream.Serial as Serial
107import qualified Streamly.Internal.Data.Stream.Serial as Serial
109import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
108import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
@@ -540,14 +539,8 @@ fromHandle h = go
540 str <- liftIO $ IO.hGetLine h
539 str <- liftIO $ IO.hGetLine h
541 yld str go
540 yld str go
542
541
543−-- | Construct a stream by reading a 'Prim' 'IORef' repeatedly.
542+-- XXX This should perhaps be moved to Parallel
544--
543--
545−-- /Internal/
546−--
547−{-# INLINE fromPrimIORef #-}
548−fromPrimIORef :: (IsStream t, MonadIO m, Prim a) => IORef a -> t m a
549−fromPrimIORef = fromStreamD . D.fromPrimIORef
550−
551-- | Takes a callback setter function and provides it with a callback. The
544-- | Takes a callback setter function and provides it with a callback. The
552-- callback when invoked adds a value at the tail of the stream. Returns a
545-- callback when invoked adds a value at the tail of the stream. Returns a
553-- stream of values generated by the callback.
546-- stream of values generated by the callback.
@@ -557,6 +550,6 @@ fromPrimIORef = fromStreamD . D.fromPrimIORef
557{-# INLINE fromCallback #-}
550{-# INLINE fromCallback #-}
558fromCallback :: MonadAsync m => ((a -> m ()) -> m ()) -> SerialT m a
551fromCallback :: MonadAsync m => ((a -> m ()) -> m ()) -> SerialT m a
559fromCallback setCallback = concatM $ do
552fromCallback setCallback = concatM $ do
560− (callback, stream) <- D.newCallbackStream
553+ (callback, stream) <- Par.newCallbackStream
561 setCallback callback
554 setCallback callback
562 return stream
555 return stream
src/Streamly/Internal/Data/Stream/IsStream/Nesting.hs+4−2
@@ -546,8 +546,10 @@ mergeAsyncBy f = mergeAsyncByM (\a b -> return $ f a b)
546{-# INLINE mergeAsyncByM #-}
546{-# INLINE mergeAsyncByM #-}
547mergeAsyncByM :: (IsStream t, MonadAsync m)
547mergeAsyncByM :: (IsStream t, MonadAsync m)
548 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
548 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
549−mergeAsyncByM f m1 m2 = fromStreamD $
549+mergeAsyncByM f m1 m2 =
550− D.mergeByM f (D.mkParallelD $ toStreamD m1) (D.mkParallelD $ toStreamD m2)
550+ fromStreamD $
551+ let par = Par.mkParallelD . toStreamD
552+ in D.mergeByM f (par m1) (par m2)
551
553
552------------------------------------------------------------------------------
554------------------------------------------------------------------------------
553-- Combine N Streams - concatMap
555-- Combine N Streams - concatMap
src/Streamly/Internal/Data/Stream/IsStream/Transform.hs+4−4
@@ -155,8 +155,7 @@ module Streamly.Internal.Data.Stream.IsStream.Transform
155 -- ** Concurrent Pipelines
155 -- ** Concurrent Pipelines
156 -- | Run streaming stages concurrently.
156 -- | Run streaming stages concurrently.
157
157
158− , D.mkParallel
159− -- Par.mkParallel
158+ , Par.mkParallel
160 , applyAsync
159 , applyAsync
161 , (|$)
160 , (|$)
162 , (|&)
161 , (|&)
@@ -219,6 +218,7 @@ import Streamly.Internal.Data.SVar (MonadAsync, Rate(..))
219import Streamly.Internal.Data.Time.Units ( TimeUnit64, AbsTime, RelTime64)
218import Streamly.Internal.Data.Time.Units ( TimeUnit64, AbsTime, RelTime64)
220
219
221import qualified Streamly.Internal.Data.Fold as FL
220import qualified Streamly.Internal.Data.Fold as FL
221+import qualified Streamly.Internal.Data.Stream.Parallel as Par
222import qualified Streamly.Internal.Data.Stream.Prelude as P
222import qualified Streamly.Internal.Data.Stream.Prelude as P
223import qualified Streamly.Internal.Data.Stream.Serial as Serial
223import qualified Streamly.Internal.Data.Stream.Serial as Serial
224import qualified Streamly.Internal.Data.Stream.StreamD as D
224import qualified Streamly.Internal.Data.Stream.StreamD as D
@@ -434,7 +434,7 @@ tapOffsetEvery offset n f xs =
434-- /Internal/
434-- /Internal/
435{-# INLINE tapAsync #-}
435{-# INLINE tapAsync #-}
436tapAsync :: (IsStream t, MonadAsync m) => FL.Fold m a b -> t m a -> t m a
436tapAsync :: (IsStream t, MonadAsync m) => FL.Fold m a b -> t m a -> t m a
437−tapAsync f xs = D.fromStreamD $ D.tapAsync f (D.toStreamD xs)
437+tapAsync f xs = D.fromStreamD $ Par.tapAsyncF f (D.toStreamD xs)
438
438
439-- | @pollCounts predicate transform fold stream@ counts those elements in the
439-- | @pollCounts predicate transform fold stream@ counts those elements in the
440-- stream that pass the @predicate@. The resulting count stream is sent to
440-- stream that pass the @predicate@. The resulting count stream is sent to
@@ -1233,7 +1233,7 @@ rights = fmap (fromRight undefined) . filter isRight
1233{-# INLINE (|$) #-}
1233{-# INLINE (|$) #-}
1234(|$) :: (IsStream t, MonadAsync m) => (t m a -> t m b) -> (t m a -> t m b)
1234(|$) :: (IsStream t, MonadAsync m) => (t m a -> t m b) -> (t m a -> t m b)
1235-- (|$) f = f . Async.mkAsync
1235-- (|$) f = f . Async.mkAsync
1236−(|$) f = f . D.mkParallel
1236+(|$) f = f . Par.mkParallel
1237
1237
1238infixr 0 |$
1238infixr 0 |$
1239
1239
src/Streamly/Internal/Data/Stream/Parallel.hs+141−74
@@ -26,10 +26,16 @@ module Streamly.Internal.Data.Stream.Parallel
26
26
27 -- * Evaluate Concurrently
27 -- * Evaluate Concurrently
28 , mkParallel
28 , mkParallel
29+ , mkParallelD
30+ , mkParallelK
29
31
30 -- * Tap Concurrently
32 -- * Tap Concurrently
31 , tapAsync
33 , tapAsync
34+ , tapAsyncF
32 , distributeAsync_
35 , distributeAsync_
36+
37+ -- * Callbacks
38+ , newCallbackStream
33 )
39 )
34where
40where
35
41
@@ -52,15 +58,17 @@ import Prelude hiding (map)
52
58
53import qualified Data.Set as Set
59import qualified Data.Set as Set
54
60
55−import Streamly.Internal.Data.Stream.SVar
61+import Streamly.Internal.Data.Fold.Types (Fold)
56− (fromSVar, fromProducer, fromConsumer, pushToFold)
62+import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
57import Streamly.Internal.Data.Stream.StreamK
63import Streamly.Internal.Data.Stream.StreamK
58 (IsStream(..), Stream, mkStream, foldStream, foldStreamShared, adapt)
64 (IsStream(..), Stream, mkStream, foldStream, foldStreamShared, adapt)
59
65
60import Streamly.Internal.Data.SVar
66import Streamly.Internal.Data.SVar
61
67
62−import qualified Streamly.Internal.Data.Stream.StreamK as K
68+import qualified Streamly.Internal.Data.Stream.StreamK as K (withLocal)
69+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
63−import qualified Streamly.Internal.Data.Stream.StreamD as D
70+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
71+import qualified Streamly.Internal.Data.Stream.SVar as SVar
64
72
65#include "Instances.hs"
73#include "Instances.hs"
66
74
@@ -145,7 +153,7 @@ forkSVarPar ss m r = mkStream $ \st yld sng stp -> do
145 writeIORef (svarStopBy sv) $ Set.elemAt 0 set
153 writeIORef (svarStopBy sv) $ Set.elemAt 0 set
146 _ -> return ()
154 _ -> return ()
147 pushWorkerPar sv (runOne st{streamVar = Just sv} $ toStream r)
155 pushWorkerPar sv (runOne st{streamVar = Just sv} $ toStream r)
148− foldStream st yld sng stp (fromSVar sv)
156+ foldStream st yld sng stp (SVar.fromSVar sv)
149
157
150{-# INLINE joinStreamVarPar #-}
158{-# INLINE joinStreamVarPar #-}
151joinStreamVarPar :: (IsStream t, MonadAsync m)
159joinStreamVarPar :: (IsStream t, MonadAsync m)
@@ -281,84 +289,63 @@ parallelMin = joinStreamVarPar ParallelVar StopAny
281-- Convert a stream to parallel
289-- Convert a stream to parallel
282------------------------------------------------------------------------------
290------------------------------------------------------------------------------
283
291
284−-- | Generate a stream asynchronously to keep it buffered, lazily consume
292+-- | Like 'mkParallel' but uses StreamK internally.
285−-- from the buffer.
286--
293--
287-- /Internal/
294-- /Internal/
288--
295--
289−mkParallel :: (IsStream t, MonadAsync m) => t m a -> t m a
296+mkParallelK :: (IsStream t, MonadAsync m) => t m a -> t m a
290−mkParallel m = mkStream $ \st yld sng stp -> do
297+mkParallelK m = mkStream $ \st yld sng stp -> do
291 sv <- newParallelVar StopNone (adaptState st)
298 sv <- newParallelVar StopNone (adaptState st)
292 -- pushWorkerPar sv (runOne st{streamVar = Just sv} $ toStream m)
299 -- pushWorkerPar sv (runOne st{streamVar = Just sv} $ toStream m)
293− D.toSVarParallel st sv $ D.toStreamD m
300+ SVar.toSVarParallel st sv $ D.toStreamD m
294− foldStream st yld sng stp $ fromSVar sv
301+ foldStream st yld sng stp $ SVar.fromSVar sv
295
302
296−------------------------------------------------------------------------------
303+-------------------------------------------------------------------------------
297−-- Clone and distribute a stream in parallel
304+-- Concurrent application and fold
298−------------------------------------------------------------------------------
305+-------------------------------------------------------------------------------
299
306
300−-- Tap a stream and send the elements to the specified SVar in addition to
307+-- | Same as 'mkParallel' but for StreamD stream.
301−-- yielding them again.
302--
308--
303−-- XXX this could be written in StreamD style for better efficiency with fusion.
309+{-# INLINE_NORMAL mkParallelD #-}
304−--
305−{-# INLINE teeToSVar #-}
306−teeToSVar :: (IsStream t, MonadAsync m) => SVar Stream m a -> t m a -> t m a
310+mkParallelD :: MonadAsync m => D.Stream m a -> D.Stream m a
307−teeToSVar svr m = mkStream $ \st yld sng stp -> do
311+mkParallelD m = D.Stream step Nothing
308− foldStreamShared st yld sng stp (go False m)
309−
310 where
312 where
311
313
312− go False m0 = mkStream $ \st yld _ stp -> do
314+ step gst Nothing = do
313− let drain = do
314− -- In general, a Stop event would come equipped with the result
315− -- of the fold. It is not used here but it would be useful in
316− -- applicative and distribute.
317− done <- fromConsumer svr
318− when (not done) $ do
319− liftIO $ withDiagMVar svr "teeToSVar: waiting to drain"
320− $ takeMVar (outputDoorBellFromConsumer svr)
321− drain
322−
323− stopFold = do
324− liftIO $ sendStop svr Nothing
325− -- drain/wait until a stop event arrives from the fold.
326− drain
327−
328− stop = stopFold >> stp
329− single a = do
330− done <- pushToFold svr a
331− yld a (go done (K.nilM stopFold))
332− yieldk a r = pushToFold svr a >>= \done -> yld a (go done r)
333− in foldStreamShared st yieldk single stop m0
334−
335− go True m0 = m0
336−
337−-- In case of folds the roles of worker and parent on an SVar are reversed. The
338−-- parent stream pushes values to an SVar instead of pulling from it and a
339−-- worker thread running the fold pulls from the SVar and folds the stream. We
340−-- keep a separate channel for pushing exceptions in the reverse direction i.e.
341−-- from the fold to the parent stream.
342−--
343−-- Note: If we terminate due to an exception, we do not actively terminate the
344−-- fold. It gets cleaned up by the GC.
345−
346−-- | Create an SVar with a fold consumer that will fold any elements sent to it
347−-- using the supplied fold function.
348−{-# INLINE newFoldSVar #-}
349−newFoldSVar :: (IsStream t, MonadAsync m)
350− => State Stream m a -> (t m a -> m b) -> m (SVar Stream m a)
351−newFoldSVar stt f = do
352− -- Buffer size for the SVar is derived from the current state
353− sv <- newParallelVar StopAny (adaptState stt)
315+ sv <- newParallelVar StopNone gst
354−
316+ SVar.toSVarParallel gst sv m
355− -- Add the producer thread-id to the SVar.
317+ -- XXX use unfold instead?
356− liftIO myThreadId >>= modifyThread sv
318+ return $ Skip $ Just $ SVar.fromSVarD sv
319+
320+ step gst (Just (D.UnStream step1 st)) = do
321+ r <- step1 gst st
322+ return $ case r of
323+ Yield a s -> Yield a (Just $ D.Stream step1 s)
324+ Skip s -> Skip (Just $ D.Stream step1 s)
325+ Stop -> Stop
357
326
358− void $ doFork (void $ f $ fromStream $ fromProducer sv)
327+-- Compare with mkAsync. mkAsync uses an Async style SVar whereas this uses a
359− (svarMrun sv)
328+-- parallel style SVar for evaluation. Currently, parallel style cannot use
360− (handleFoldException sv)
329+-- rate control whereas Async style can use rate control. In async style SVar
361− return sv
330+-- the worker thread terminates when the buffer is full whereas in Parallel
331+-- style it blocks.
332+--
333+-- | Make the stream producer and consumer run concurrently by introducing a
334+-- buffer between them. The producer thread evaluates the input stream until
335+-- the buffer fills, it blocks if the buffer is full until there is space in
336+-- the buffer. The consumer consumes the stream lazily from the buffer.
337+--
338+-- @mkParallel = D.fromStreamD . mkParallelD . D.toStreamD@
339+--
340+-- /Internal/
341+--
342+{-# INLINE_NORMAL mkParallel #-}
343+mkParallel :: (K.IsStream t, MonadAsync m) => t m a -> t m a
344+mkParallel = D.fromStreamD . mkParallelD . D.toStreamD
345+
346+-------------------------------------------------------------------------------
347+-- Concurrent tap
348+-------------------------------------------------------------------------------
362
349
363-- NOTE: In regular pull style streams, the consumer stream is pulling elements
350-- NOTE: In regular pull style streams, the consumer stream is pulling elements
364-- from the SVar and we have several workers producing elements and pushing to
351-- from the SVar and we have several workers producing elements and pushing to
@@ -404,8 +391,60 @@ newFoldSVar stt f = do
404{-# INLINE tapAsync #-}
391{-# INLINE tapAsync #-}
405tapAsync :: (IsStream t, MonadAsync m) => (t m a -> m b) -> t m a -> t m a
392tapAsync :: (IsStream t, MonadAsync m) => (t m a -> m b) -> t m a -> t m a
406tapAsync f m = mkStream $ \st yld sng stp -> do
393tapAsync f m = mkStream $ \st yld sng stp -> do
407− sv <- newFoldSVar st f
394+ sv <- SVar.newFoldSVar st f
408− foldStreamShared st yld sng stp (teeToSVar sv m)
395+ foldStreamShared st yld sng stp (SVar.teeToSVar sv m)
396+
397+data TapState fs st a = TapInit | Tapping !fs st | TapDone st
398+
399+-- | Like 'tapAsync' but uses a 'Fold' instead of a fold function.
400+--
401+{-# INLINE_NORMAL tapAsyncF #-}
402+tapAsyncF :: MonadAsync m => Fold m a b -> D.Stream m a -> D.Stream m a
403+tapAsyncF f (D.Stream step1 state1) = D.Stream step TapInit
404+ where
405+
406+ drainFold svr = do
407+ -- In general, a Stop event would come equipped with the result
408+ -- of the fold. It is not used here but it would be useful in
409+ -- applicative and distribute.
410+ done <- SVar.fromConsumer svr
411+ when (not done) $ do
412+ liftIO $ withDiagMVar svr "teeToSVar: waiting to drain"
413+ $ takeMVar (outputDoorBellFromConsumer svr)
414+ drainFold svr
415+
416+ stopFold svr = do
417+ liftIO $ sendStop svr Nothing
418+ -- drain/wait until a stop event arrives from the fold.
419+ drainFold svr
420+
421+ {-# INLINE_LATE step #-}
422+ step gst TapInit = do
423+ sv <- SVar.newFoldSVarF gst f
424+ return $ Skip (Tapping sv state1)
425+
426+ step gst (Tapping sv st) = do
427+ r <- step1 gst st
428+ case r of
429+ Yield a s -> do
430+ done <- SVar.pushToFold sv a
431+ if done
432+ then do
433+ -- XXX we do not need to wait synchronously here
434+ stopFold sv
435+ return $ Yield a (TapDone s)
436+ else return $ Yield a (Tapping sv s)
437+ Skip s -> return $ Skip (Tapping sv s)
438+ Stop -> do
439+ stopFold sv
440+ return Stop
441+
442+ step gst (TapDone st) = do
443+ r <- step1 gst st
444+ return $ case r of
445+ Yield a s -> Yield a (TapDone s)
446+ Skip s -> Skip (TapDone s)
447+ Stop -> Stop
409
448
410-- | Concurrently distribute a stream to a collection of fold functions,
449-- | Concurrently distribute a stream to a collection of fold functions,
411-- discarding the outputs of the folds.
450-- discarding the outputs of the folds.
@@ -582,3 +621,31 @@ instance MonadAsync m => Monad (ParallelT m) where
582------------------------------------------------------------------------------
621------------------------------------------------------------------------------
583
622
584MONAD_COMMON_INSTANCES(ParallelT, MONADPARALLEL)
623MONAD_COMMON_INSTANCES(ParallelT, MONADPARALLEL)
624+
625+-------------------------------------------------------------------------------
626+-- From callback
627+-------------------------------------------------------------------------------
628+
629+-- Note: we can use another API with two callbacks stop and yield if we want
630+-- the callback to be able to indicate end of stream.
631+--
632+-- | Generates a callback and a stream pair. The callback returned is used to
633+-- queue values to the stream. The stream is infinite, there is no way for the
634+-- callback to indicate that it is done now.
635+--
636+-- /Internal/
637+--
638+{-# INLINE_NORMAL newCallbackStream #-}
639+newCallbackStream :: (K.IsStream t, MonadAsync m) => m (a -> m (), t m a)
640+newCallbackStream = do
641+ sv <- newParallelVar StopNone defState
642+
643+ -- XXX Add our own thread-id to the SVar as we can not know the callback's
644+ -- thread-id and the callback is not run in a managed worker. We need to
645+ -- handle this better.
646+ liftIO myThreadId >>= modifyThread sv
647+
648+ let callback a = liftIO $ void $ send sv (ChildYield a)
649+ -- XXX we can return an SVar and then the consumer can unfold from the
650+ -- SVar?
651+ return (callback, D.fromStreamD (SVar.fromSVarD sv))
src/Streamly/Internal/Data/Stream/SVar.hs+479−33
@@ -18,26 +18,46 @@
18--
18--
19--
19--
20module Streamly.Internal.Data.Stream.SVar
20module Streamly.Internal.Data.Stream.SVar
21+ (
22+ -- * Unfold streams from SVar
23+ -- $concurrentEval
21− ( fromSVar
24+ fromSVar
25+ , fromSVarD
22− , fromStreamVar
26+ -- , fromStreamVar
23− , fromProducer
27+
24− , fromConsumer
28+ -- * Fold streams to SVar
25 , toSVar
29 , toSVar
30+ , toSVarParallel
31+
32+ -- * Concurrent folds
33+ -- $concurrentFolds
34+ , fromConsumer
26 , pushToFold
35 , pushToFold
36+ , teeToSVar
37+ , newFoldSVar
38+ , newFoldSVarF
27 )
39 )
28where
40where
29
41
42+#include "inline.hs"
43+
44+import Control.Concurrent (myThreadId, takeMVar)
30import Control.Exception (fromException)
45import Control.Exception (fromException)
31import Control.Monad (when, void)
46import Control.Monad (when, void)
32import Control.Monad.Catch (throwM)
47import Control.Monad.Catch (throwM)
33import Control.Monad.IO.Class (MonadIO(liftIO))
48import Control.Monad.IO.Class (MonadIO(liftIO))
34import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
49import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
35import Data.Maybe (isNothing)
50import Data.Maybe (isNothing)
51+import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
52+import Streamly.Internal.Data.Fold.Types (Fold(..))
36import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
53import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
37import System.Mem (performMajorGC)
54import System.Mem (performMajorGC)
38
55
56+import qualified Streamly.Internal.Data.Fold.Types as FL
57+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
58+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
59+
39import Streamly.Internal.Data.SVar
60import Streamly.Internal.Data.SVar
40−import Streamly.Internal.Data.Stream.StreamK hiding (reverse)
41
61
42#if __GLASGOW_HASKELL__ < 810
62#if __GLASGOW_HASKELL__ < 810
43#ifdef INSPECTION
63#ifdef INSPECTION
@@ -49,15 +69,39 @@ import Test.Inspection (inspect, hasNoTypeClassesExcept)
49#endif
69#endif
50#endif
70#endif
51
71
72+------------------------------------------------------------------------------
73+-- Generating streams from SVar
74+------------------------------------------------------------------------------
75+
76+-- $concurrentEval
77+--
78+-- Usually the SVar is used to concurrently evaluate multiple actions in a
79+-- stream using many worker threads that push the results to the SVar and a
80+-- single puller that pulls them from SVar generating the evaluated stream.
81+--
82+-- @
83+-- input stream
84+-- |
85+-- <-----------------|<--------worker
86+-- | exceptions |
87+-- output stream <------SVar<------worker
88+-- |
89+-- |<--------worker
90+--
91+-- @
92+--
93+-- The puller itself schedules the worker threads based on demand.
94+-- Exceptions are propagated from the worker threads to the puller.
95+
52-- | Pull a stream from an SVar.
96-- | Pull a stream from an SVar.
53{-# NOINLINE fromStreamVar #-}
97{-# NOINLINE fromStreamVar #-}
54−fromStreamVar :: MonadAsync m => SVar Stream m a -> Stream m a
98+fromStreamVar :: MonadAsync m => SVar K.Stream m a -> K.Stream m a
55−fromStreamVar sv = MkStream $ \st yld sng stp -> do
99+fromStreamVar sv = K.MkStream $ \st yld sng stp -> do
56 list <- readOutputQ sv
100 list <- readOutputQ sv
57 -- Reversing the output is important to guarantee that we process the
101 -- Reversing the output is important to guarantee that we process the
58 -- outputs in the same order as they were generated by the constituent
102 -- outputs in the same order as they were generated by the constituent
59 -- streams.
103 -- streams.
60− foldStream st yld sng stp $ processEvents $ reverse list
104+ K.foldStream st yld sng stp $ processEvents $ reverse list
61
105
62 where
106 where
63
107
@@ -69,13 +113,13 @@ fromStreamVar sv = MkStream $ \st yld sng stp -> do
69 stp
113 stp
70
114
71 {-# INLINE processEvents #-}
115 {-# INLINE processEvents #-}
72− processEvents [] = MkStream $ \st yld sng stp -> do
116+ processEvents [] = K.MkStream $ \st yld sng stp -> do
73 done <- postProcess sv
117 done <- postProcess sv
74 if done
118 if done
75 then allDone stp
119 then allDone stp
76− else foldStream st yld sng stp $ fromStreamVar sv
120+ else K.foldStream st yld sng stp $ fromStreamVar sv
77
121
78− processEvents (ev : es) = MkStream $ \st yld sng stp -> do
122+ processEvents (ev : es) = K.MkStream $ \st yld sng stp -> do
79 let rest = processEvents es
123 let rest = processEvents es
80 case ev of
124 case ev of
81 ChildYield a -> yld a rest
125 ChildYield a -> yld a rest
@@ -86,11 +130,11 @@ fromStreamVar sv = MkStream $ \st yld sng stp -> do
86 stop <- shouldStop tid
130 stop <- shouldStop tid
87 if stop
131 if stop
88 then liftIO (cleanupSVar sv) >> allDone stp
132 then liftIO (cleanupSVar sv) >> allDone stp
89− else foldStream st yld sng stp rest
133+ else K.foldStream st yld sng stp rest
90 Just ex ->
134 Just ex ->
91 case fromException ex of
135 case fromException ex of
92 Just ThreadAbort ->
136 Just ThreadAbort ->
93− foldStream st yld sng stp rest
137+ K.foldStream st yld sng stp rest
94 Nothing -> liftIO (cleanupSVar sv) >> throwM ex
138 Nothing -> liftIO (cleanupSVar sv) >> throwM ex
95 shouldStop tid =
139 shouldStop tid =
96 case svarStopStyle sv of
140 case svarStopStyle sv of
@@ -98,7 +142,7 @@ fromStreamVar sv = MkStream $ \st yld sng stp -> do
98 StopAny -> return True
142 StopAny -> return True
99 StopBy -> do
143 StopBy -> do
100 sid <- liftIO $ readIORef (svarStopBy sv)
144 sid <- liftIO $ readIORef (svarStopBy sv)
101− return $ if tid == sid then True else False
145+ return $ tid == sid
102
146
103#if __GLASGOW_HASKELL__ < 810
147#if __GLASGOW_HASKELL__ < 810
104#ifdef INSPECTION
148#ifdef INSPECTION
@@ -120,17 +164,23 @@ inspect $ hasNoTypeClassesExcept 'fromStreamVar
120#endif
164#endif
121#endif
165#endif
122
166
167+-- | Generate a stream from an SVar. An unevaluated stream can be pushed to an
168+-- SVar using 'toSVar'. As we pull a stream from the SVar the input stream
169+-- gets evaluated concurrently. The evaluation depends on the SVar style and
170+-- the configuration parameters e.g. using the maxBuffer/maxThreads
171+-- combinators.
172+--
123{-# INLINE fromSVar #-}
173{-# INLINE fromSVar #-}
124−fromSVar :: (MonadAsync m, IsStream t) => SVar Stream m a -> t m a
174+fromSVar :: (MonadAsync m, K.IsStream t) => SVar K.Stream m a -> t m a
125fromSVar sv =
175fromSVar sv =
126− mkStream $ \st yld sng stp -> do
176+ K.mkStream $ \st yld sng stp -> do
127 ref <- liftIO $ newIORef ()
177 ref <- liftIO $ newIORef ()
128 _ <- liftIO $ mkWeakIORef ref hook
178 _ <- liftIO $ mkWeakIORef ref hook
129 -- We pass a copy of sv to fromStreamVar, so that we know that it has
179 -- We pass a copy of sv to fromStreamVar, so that we know that it has
130 -- no other references, when that copy gets garbage collected "ref"
180 -- no other references, when that copy gets garbage collected "ref"
131 -- will get garbage collected and our hook will be called.
181 -- will get garbage collected and our hook will be called.
132− foldStreamShared st yld sng stp $
182+ K.foldStreamShared st yld sng stp $
133− fromStream $ fromStreamVar sv{svarRef = Just ref}
183+ K.fromStream $ fromStreamVar sv{svarRef = Just ref}
134 where
184 where
135
185
136 hook = do
186 hook = do
@@ -143,24 +193,281 @@ fromSVar sv =
143 -- them to be cleaned up quickly.
193 -- them to be cleaned up quickly.
144 when (svarInspectMode sv) performMajorGC
194 when (svarInspectMode sv) performMajorGC
145
195
196+data FromSVarState t m a =
197+ FromSVarInit
198+ | FromSVarRead (SVar t m a)
199+ | FromSVarLoop (SVar t m a) [ChildEvent a]
200+ | FromSVarDone (SVar t m a)
201+
202+-- | Like 'fromSVar' but generates a StreamD style stream instead of CPS.
203+--
204+{-# INLINE_NORMAL fromSVarD #-}
205+fromSVarD :: (MonadAsync m) => SVar t m a -> D.Stream m a
206+fromSVarD svar = D.Stream step FromSVarInit
207+ where
208+
209+ {-# INLINE_LATE step #-}
210+ step _ FromSVarInit = do
211+ ref <- liftIO $ newIORef ()
212+ _ <- liftIO $ mkWeakIORef ref hook
213+ -- when this copy of svar gets garbage collected "ref" will get
214+ -- garbage collected and our GC hook will be called.
215+ let sv = svar{svarRef = Just ref}
216+ return $ D.Skip (FromSVarRead sv)
217+
218+ where
219+
220+ {-# NOINLINE hook #-}
221+ hook = do
222+ when (svarInspectMode svar) $ do
223+ r <- liftIO $ readIORef (svarStopTime (svarStats svar))
224+ when (isNothing r) $
225+ printSVar svar "SVar Garbage Collected"
226+ cleanupSVar svar
227+ -- If there are any SVars referenced by this SVar a GC will prompt
228+ -- them to be cleaned up quickly.
229+ when (svarInspectMode svar) performMajorGC
230+
231+ step _ (FromSVarRead sv) = do
232+ list <- readOutputQ sv
233+ -- Reversing the output is important to guarantee that we process the
234+ -- outputs in the same order as they were generated by the constituent
235+ -- streams.
236+ return $ D.Skip $ FromSVarLoop sv (Prelude.reverse list)
237+
238+ step _ (FromSVarLoop sv []) = do
239+ done <- postProcess sv
240+ return $ D.Skip $ if done
241+ then FromSVarDone sv
242+ else FromSVarRead sv
243+
244+ step _ (FromSVarLoop sv (ev : es)) = do
245+ case ev of
246+ ChildYield a -> return $ D.Yield a (FromSVarLoop sv es)
247+ ChildStop tid e -> do
248+ accountThread sv tid
249+ case e of
250+ Nothing -> do
251+ stop <- shouldStop tid
252+ if stop
253+ then do
254+ liftIO (cleanupSVar sv)
255+ return $ D.Skip (FromSVarDone sv)
256+ else return $ D.Skip (FromSVarLoop sv es)
257+ Just ex ->
258+ case fromException ex of
259+ Just ThreadAbort ->
260+ return $ D.Skip (FromSVarLoop sv es)
261+ Nothing -> liftIO (cleanupSVar sv) >> throwM ex
262+ where
263+
264+ shouldStop tid =
265+ case svarStopStyle sv of
266+ StopNone -> return False
267+ StopAny -> return True
268+ StopBy -> do
269+ sid <- liftIO $ readIORef (svarStopBy sv)
270+ return $ tid == sid
271+
272+ step _ (FromSVarDone sv) = do
273+ when (svarInspectMode sv) $ do
274+ t <- liftIO $ getTime Monotonic
275+ liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
276+ liftIO $ printSVar sv "SVar Done"
277+ return D.Stop
278+
279+------------------------------------------------------------------------------
280+-- Writing streams to SVar
281+------------------------------------------------------------------------------
282+
146−-- | Write a stream to an 'SVar' in a non-blocking manner. The stream can then
283+-- | Write a stream to an 'SVar' in a non-blocking manner. The stream is
147−-- be read back from the SVar using 'fromSVar'.
284+-- evaluated concurrently as it is read back from the SVar using 'fromSVar'.
285+--
148−toSVar :: (IsStream t, MonadAsync m) => SVar Stream m a -> t m a -> m ()
286+toSVar :: (K.IsStream t, MonadAsync m) => SVar K.Stream m a -> t m a -> m ()
149−toSVar sv m = toStreamVar sv $ toStream m
287+toSVar sv m = toStreamVar sv $ K.toStream m
288+
289+-------------------------------------------------------------------------------
290+-- Concurrent application
291+-------------------------------------------------------------------------------
292+
293+-- | A fold to write a stream to an SVar. Unlike 'toSVar' this does not allow
294+-- for concurrent evaluation of the stream, as the fold receives the input one
295+-- element at a time, it just forwards the elements to the SVar. However, we
296+-- can safely execute the fold in an independent thread, the SVar can act as a
297+-- buffer decoupling the sender from the receiver. Also, we can have multiple
298+-- folds running concurrently pusing the streams to the SVar.
299+--
300+{-# INLINE write #-}
301+write :: MonadIO m => SVar t m a -> Maybe WorkerInfo -> Fold m a ()
302+write svar winfo = Fold step initial extract
303+
304+ where
305+
306+ initial = return $ FL.Partial ()
307+
308+ -- XXX we can have a separate fold for unlimited buffer case to avoid a
309+ -- branch in the step here.
310+ step () x =
311+ liftIO $ do
312+ decrementBufferLimit svar
313+ void $ send svar (ChildYield x)
314+ return $ FL.Partial ()
315+
316+ extract () = liftIO $ sendStop svar winfo
317+
318+-- | Like write, but applies a yield limit.
319+--
320+{-# INLINE writeLimited #-}
321+writeLimited :: MonadIO m
322+ => SVar t m a -> Maybe WorkerInfo -> Fold m a ()
323+writeLimited svar winfo = Fold step initial extract
324+
325+ where
326+
327+ initial = return $ FL.Partial True
328+
329+ step True x =
330+ liftIO $ do
331+ yieldLimitOk <- decrementYieldLimit svar
332+ if yieldLimitOk
333+ then do
334+ decrementBufferLimit svar
335+ void $ send svar (ChildYield x)
336+ return $ FL.Partial True
337+ else do
338+ cleanupSVarFromWorker svar
339+ sendStop svar winfo
340+ return $ FL.Done ()
341+ step False _ = return $ FL.Done ()
342+
343+ extract True = liftIO $ sendStop svar winfo
344+ extract False = return ()
345+
346+-- Using StreamD the worker stream producing code can fuse with the code to
347+-- queue output to the SVar giving some perf boost.
348+--
349+-- Note that StreamD can only be used in limited situations, specifically, we
350+-- cannot implement joinStreamVarPar using this.
351+--
352+-- XXX make sure that the SVar passed is a Parallel style SVar.
353+
354+-- | Fold the supplied stream to the SVar asynchronously using Parallel
355+-- concurrency style.
356+-- {-# INLINE_NORMAL toSVarParallel #-}
357+{-# INLINE toSVarParallel #-}
358+toSVarParallel :: MonadAsync m
359+ => State t m a -> SVar t m a -> D.Stream m a -> m ()
360+toSVarParallel st sv xs =
361+ if svarInspectMode sv
362+ then forkWithDiag
363+ else do
364+ tid <-
365+ case getYieldLimit st of
366+ Nothing -> doFork (work Nothing)
367+ (svarMrun sv)
368+ (handleChildException sv)
369+ Just _ -> doFork (workLim Nothing)
370+ (svarMrun sv)
371+ (handleChildException sv)
372+ modifyThread sv tid
373+
374+ where
375+
376+ {-# NOINLINE work #-}
377+ work info = D.foldOnce (write sv info) xs
378+
379+ {-# NOINLINE workLim #-}
380+ workLim info = D.foldOnce (writeLimited sv info) xs
381+
382+ {-# NOINLINE forkWithDiag #-}
383+ forkWithDiag = do
384+ -- We do not use workerCount in case of ParallelVar but still there is
385+ -- no harm in maintaining it correctly.
386+ liftIO $ atomicModifyIORefCAS_ (workerCount sv) $ \n -> n + 1
387+ recordMaxWorkers sv
388+ -- This allocation matters when significant number of workers are being
389+ -- sent. We allocate it only when needed. The overhead increases by 4x.
390+ winfo <-
391+ case yieldRateInfo sv of
392+ Nothing -> return Nothing
393+ Just _ -> liftIO $ do
394+ cntRef <- newIORef 0
395+ t <- getTime Monotonic
396+ lat <- newIORef (0, t)
397+ return $ Just WorkerInfo
398+ { workerYieldMax = 0
399+ , workerYieldCount = cntRef
400+ , workerLatencyStart = lat
401+ }
402+ tid <-
403+ case getYieldLimit st of
404+ Nothing -> doFork (work winfo)
405+ (svarMrun sv)
406+ (handleChildException sv)
407+ Just _ -> doFork (workLim winfo)
408+ (svarMrun sv)
409+ (handleChildException sv)
410+ modifyThread sv tid
411+
412+-------------------------------------------------------------------------------
413+-- Support for running folds concurrently
414+-------------------------------------------------------------------------------
415+
416+-- $concurrentFolds
417+--
418+-- To run folds concurrently, we need to decouple the fold execution from the
419+-- stream production. We use the SVar to do that, we have a single worker
420+-- pushing the stream elements to the SVar and on the consumer side a fold
421+-- driver pulls the values and folds them.
422+--
423+-- @
424+--
425+-- Fold worker <------SVar<------input stream
426+-- | exceptions |
427+-- --------------->
428+--
429+-- @
430+--
431+-- We need a channel for pushing exceptions from the fold worker to the stream
432+-- pusher. The stream may be pushed to multiple folds at the same time. For
433+-- that we need one SVar per fold:
434+--
435+-- @
436+--
437+-- Fold worker <------SVar<---
438+-- | |
439+-- Fold worker <------SVar<------input stream
440+-- | |
441+-- Fold worker <------SVar<---
442+--
443+-- @
444+--
445+-- Unlike in case concurrent stream evaluation, the puller does not drive the
446+-- scheduling and concurrent execution of the stream. The stream is simply
447+-- pushed by the stream producer at its own rate. The fold worker just pulls it
448+-- and folds it.
449+--
450+-- Note: If the stream pusher terminates due to an exception, we do not
451+-- actively terminate the fold. It gets cleaned up by the GC.
150
452
151-------------------------------------------------------------------------------
453-------------------------------------------------------------------------------
152-- Process events received by a fold consumer from a stream producer
454-- Process events received by a fold consumer from a stream producer
153-------------------------------------------------------------------------------
455-------------------------------------------------------------------------------
154
456
155−-- | Pull a stream from an SVar.
457+-- | Pull a stream from an SVar to fold it. Like 'fromSVar' except that it does
458+-- not drive the evaluation of the stream. It just pulls whatever is available
459+-- on the SVar. Also, when the fold stops it sends a notification to the stream
460+-- pusher/producer. No exceptions are expected to be propagated from the stream
461+-- pusher to the fold puller.
462+--
156{-# NOINLINE fromProducer #-}
463{-# NOINLINE fromProducer #-}
157−fromProducer :: forall m a . MonadAsync m => SVar Stream m a -> Stream m a
464+fromProducer :: forall m a . MonadAsync m => SVar K.Stream m a -> K.Stream m a
158−fromProducer sv = mkStream $ \st yld sng stp -> do
465+fromProducer sv = K.mkStream $ \st yld sng stp -> do
159 list <- readOutputQ sv
466 list <- readOutputQ sv
160 -- Reversing the output is important to guarantee that we process the
467 -- Reversing the output is important to guarantee that we process the
161 -- outputs in the same order as they were generated by the constituent
468 -- outputs in the same order as they were generated by the constituent
162 -- streams.
469 -- streams.
163− foldStream st yld sng stp $ processEvents $ reverse list
470+ K.foldStream st yld sng stp $ processEvents $ reverse list
164
471
165 where
472 where
166
473
@@ -174,11 +481,11 @@ fromProducer sv = mkStream $ \st yld sng stp -> do
174 stp
481 stp
175
482
176 {-# INLINE processEvents #-}
483 {-# INLINE processEvents #-}
177− processEvents :: [ChildEvent a] -> Stream m a
484+ processEvents :: [ChildEvent a] -> K.Stream m a
178− processEvents [] = mkStream $ \st yld sng stp -> do
485+ processEvents [] = K.mkStream $ \st yld sng stp -> do
179− foldStream st yld sng stp $ fromProducer sv
486+ K.foldStream st yld sng stp $ fromProducer sv
180
487
181− processEvents (ev : es) = mkStream $ \_ yld _ stp -> do
488+ processEvents (ev : es) = K.mkStream $ \_ yld _ stp -> do
182 let rest = processEvents es
489 let rest = processEvents es
183 case ev of
490 case ev of
184 ChildYield a -> yld a rest
491 ChildYield a -> yld a rest
@@ -188,6 +495,80 @@ fromProducer sv = mkStream $ \st yld sng stp -> do
188 Nothing -> allDone stp
495 Nothing -> allDone stp
189 Just _ -> error "Bug: fromProducer: received exception"
496 Just _ -> error "Bug: fromProducer: received exception"
190
497
498+-- | Create a Fold style SVar that runs a supplied fold function as the
499+-- consumer. Any elements sent to the SVar are consumed by the supplied fold
500+-- function.
501+--
502+{-# INLINE newFoldSVar #-}
503+newFoldSVar :: (K.IsStream t, MonadAsync m)
504+ => State K.Stream m a -> (t m a -> m b) -> m (SVar K.Stream m a)
505+newFoldSVar stt f = do
506+ -- Buffer size for the SVar is derived from the current state
507+ sv <- newParallelVar StopAny (adaptState stt)
508+
509+ -- Add the producer thread-id to the SVar.
510+ liftIO myThreadId >>= modifyThread sv
511+
512+ void $ doFork (void $ f $ K.fromStream $ fromProducer sv)
513+ (svarMrun sv)
514+ (handleFoldException sv)
515+ return sv
516+
517+-- | Like 'fromProducer' but generates a StreamD style stream instead of
518+-- StreamK.
519+--
520+{-# INLINE_NORMAL fromProducerD #-}
521+fromProducerD :: (MonadAsync m) => SVar t m a -> D.Stream m a
522+fromProducerD svar = D.Stream step (FromSVarRead svar)
523+ where
524+
525+ {-# INLINE_LATE step #-}
526+ step _ (FromSVarRead sv) = do
527+ list <- readOutputQ sv
528+ -- Reversing the output is important to guarantee that we process the
529+ -- outputs in the same order as they were generated by the constituent
530+ -- streams.
531+ return $ D.Skip $ FromSVarLoop sv (Prelude.reverse list)
532+
533+ step _ (FromSVarLoop sv []) = return $ D.Skip $ FromSVarRead sv
534+ step _ (FromSVarLoop sv (ev : es)) = do
535+ case ev of
536+ ChildYield a -> return $ D.Yield a (FromSVarLoop sv es)
537+ ChildStop tid e -> do
538+ accountThread sv tid
539+ case e of
540+ Nothing -> do
541+ sendStopToProducer sv
542+ return $ D.Skip (FromSVarDone sv)
543+ Just _ -> error "Bug: fromProducer: received exception"
544+
545+ step _ (FromSVarDone sv) = do
546+ when (svarInspectMode sv) $ do
547+ t <- liftIO $ getTime Monotonic
548+ liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
549+ liftIO $ printSVar sv "SVar Done"
550+ return D.Stop
551+
552+ step _ FromSVarInit = undefined
553+
554+-- | Like 'newFoldSVar' except that it uses a 'Fold' instead of a fold
555+-- function.
556+--
557+{-# INLINE newFoldSVarF #-}
558+newFoldSVarF :: MonadAsync m => State t m a -> Fold m a b -> m (SVar t m a)
559+newFoldSVarF stt f = do
560+ -- Buffer size for the SVar is derived from the current state
561+ sv <- newParallelVar StopAny (adaptState stt)
562+ -- Add the producer thread-id to the SVar.
563+ liftIO myThreadId >>= modifyThread sv
564+ void $ doFork (work sv) (svarMrun sv) (handleFoldException sv)
565+ return sv
566+
567+ where
568+
569+ {-# NOINLINE work #-}
570+ work sv = void $ D.foldOnce f $ fromProducerD sv
571+
191-------------------------------------------------------------------------------
572-------------------------------------------------------------------------------
192-- Process events received by the producer thread from the consumer side
573-- Process events received by the producer thread from the consumer side
193-------------------------------------------------------------------------------
574-------------------------------------------------------------------------------
@@ -197,8 +578,13 @@ fromProducer sv = mkStream $ \st yld sng stp -> do
197-- generate exception more than once and the producer can ignore those
578-- generate exception more than once and the producer can ignore those
198-- exceptions or handle them and still keep driving the fold.
579-- exceptions or handle them and still keep driving the fold.
199--
580--
581+-- | Poll for events sent by the fold consumer to the stream pusher. The fold
582+-- consumer can send a "Stop" event or an exception. When a "Stop" is received
583+-- this function returns 'True'. If an exception is recieved then it throws the
584+-- exception.
585+--
200{-# NOINLINE fromConsumer #-}
586{-# NOINLINE fromConsumer #-}
201−fromConsumer :: MonadAsync m => SVar Stream m a -> m Bool
587+fromConsumer :: MonadAsync m => SVar K.Stream m a -> m Bool
202fromConsumer sv = do
588fromConsumer sv = do
203 (list, _) <- liftIO $ readOutputQBasic (outputQueueFromConsumer sv)
589 (list, _) <- liftIO $ readOutputQBasic (outputQueueFromConsumer sv)
204 -- Reversing the output is important to guarantee that we process the
590 -- Reversing the output is important to guarantee that we process the
@@ -218,9 +604,13 @@ fromConsumer sv = do
218 Just ex -> throwM ex
604 Just ex -> throwM ex
219 ChildYield _ -> error "Bug: fromConsumer: invalid ChildYield event"
605 ChildYield _ -> error "Bug: fromConsumer: invalid ChildYield event"
220
606
221−-- push values to a fold worker via an SVar. Returns whether the fold is done.
607+-- | Push values from a stream to a fold worker via an SVar. Before pushing a
608+-- value to the SVar it polls for events received from the fold consumer. If a
609+-- stop event is received then it returns 'True' otherwise false. Propagates
610+-- exceptions received from the fold consumer.
611+--
222{-# INLINE pushToFold #-}
612{-# INLINE pushToFold #-}
223−pushToFold :: MonadAsync m => SVar Stream m a -> a -> m Bool
613+pushToFold :: MonadAsync m => SVar K.Stream m a -> a -> m Bool
224pushToFold sv a = do
614pushToFold sv a = do
225 -- Check for exceptions before decrement so that we do not
615 -- Check for exceptions before decrement so that we do not
226 -- block forever if the child already exited with an exception.
616 -- block forever if the child already exited with an exception.
@@ -241,3 +631,59 @@ pushToFold sv a = do
241 decrementBufferLimit sv
631 decrementBufferLimit sv
242 void $ send sv (ChildYield a)
632 void $ send sv (ChildYield a)
243 return False
633 return False
634+
635+------------------------------------------------------------------------------
636+-- Clone and distribute a stream in parallel
637+------------------------------------------------------------------------------
638+
639+-- XXX this could be written in StreamD style for better efficiency with fusion.
640+--
641+-- | Tap a stream and send the elements to the specified SVar in addition to
642+-- yielding them again. The SVar runs a fold consumer. Elements are tapped and
643+-- sent to the SVar until the fold finishes. Any exceptions from the fold
644+-- evaluation are propagated in the current thread.
645+--
646+-- @
647+--
648+-- ------input stream---------output stream----->
649+-- /|\\ |
650+-- exceptions | | input
651+-- | \\|/
652+-- ----SVar
653+-- |
654+-- Fold
655+--
656+-- @
657+--
658+{-# INLINE teeToSVar #-}
659+teeToSVar :: (K.IsStream t, MonadAsync m) =>
660+ SVar K.Stream m a -> t m a -> t m a
661+teeToSVar svr m = K.mkStream $ \st yld sng stp -> do
662+ K.foldStreamShared st yld sng stp (go False m)
663+
664+ where
665+
666+ go False m0 = K.mkStream $ \st yld _ stp -> do
667+ let drain = do
668+ -- In general, a Stop event would come equipped with the result
669+ -- of the fold. It is not used here but it would be useful in
670+ -- applicative and distribute.
671+ done <- fromConsumer svr
672+ when (not done) $ do
673+ liftIO $ withDiagMVar svr "teeToSVar: waiting to drain"
674+ $ takeMVar (outputDoorBellFromConsumer svr)
675+ drain
676+
677+ stopFold = do
678+ liftIO $ sendStop svr Nothing
679+ -- drain/wait until a stop event arrives from the fold.
680+ drain
681+
682+ stop = stopFold >> stp
683+ single a = do
684+ done <- pushToFold svr a
685+ yld a (go done (K.nilM stopFold))
686+ yieldk a r = pushToFold svr a >>= \done -> yld a (go done r)
687+ in K.foldStreamShared st yieldk single stop m0
688+
689+ go True m0 = m0
src/Streamly/Internal/Data/Stream/Serial.hs+6−3
@@ -64,13 +64,16 @@ import Text.Read
64 ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec
64 ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec
65 , readListPrecDefault)
65 , readListPrecDefault)
66import Streamly.Internal.BaseCompat ((#.), errorWithoutStackTrace)
66import Streamly.Internal.BaseCompat ((#.), errorWithoutStackTrace)
67−import Streamly.Internal.Data.Stream.StreamK
67+import Streamly.Internal.Data.Stream.StreamK.Type
68 (IsStream(..), adapt, Stream, mkStream, foldStream)
68 (IsStream(..), adapt, Stream, mkStream, foldStream)
69import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
69import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
70
70
71import qualified Streamly.Internal.Data.Stream.Prelude as P
71import qualified Streamly.Internal.Data.Stream.Prelude as P
72+ (cmpBy, foldl', foldr, eqBy, fromList, toList)
72−import qualified Streamly.Internal.Data.Stream.StreamK as K
73+import qualified Streamly.Internal.Data.Stream.StreamK as K (withLocal)
73−import qualified Streamly.Internal.Data.Stream.StreamD as D
74+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
75+import qualified Streamly.Internal.Data.Stream.StreamD as D (unfoldrM)
76+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
74
77
75import Prelude hiding (map, mapM, errorWithoutStackTrace)
78import Prelude hiding (map, mapM, errorWithoutStackTrace)
76
79
src/Streamly/Internal/Data/Stream/StreamD/Eliminate.hs−1
@@ -59,7 +59,6 @@ module Streamly.Internal.Data.Stream.StreamD.Eliminate
59 -- * To containers
59 -- * To containers
60 , toList
60 , toList
61 , toListRev
61 , toListRev
62− , toSVarParallel
63
62
64 -- * Multi-Stream Folds
63 -- * Multi-Stream Folds
65 -- ** Comparisons
64 -- ** Comparisons
src/Streamly/Internal/Data/Stream/StreamD/Generate.hs+2−37
@@ -78,12 +78,6 @@ module Streamly.Internal.Data.Stream.StreamD.Generate
78 -- Note: Direct style stream does not support @fromFoldable@.
78 -- Note: Direct style stream does not support @fromFoldable@.
79 , fromList
79 , fromList
80 , fromListM
80 , fromListM
81− , fromPrimIORef
82− , fromProducer
83− , fromSVar
84−
85− -- * Callbacks
86− , newCallbackStream
87
81
88 -- * Conversions
82 -- * Conversions
89 , fromStreamK
83 , fromStreamK
@@ -95,8 +89,8 @@ where
95
89
96#include "inline.hs"
90#include "inline.hs"
97
91
98−import Control.Concurrent (myThreadId, threadDelay)
92+import Control.Concurrent (threadDelay)
99−import Control.Monad (void, forever)
93+import Control.Monad (forever)
100import Control.Monad.IO.Class (MonadIO(..))
94import Control.Monad.IO.Class (MonadIO(..))
101import Data.Int (Int64)
95import Data.Int (Int64)
102import Streamly.Internal.Data.Time.Units (toRelTime64, RelTime64)
96import Streamly.Internal.Data.Time.Units (toRelTime64, RelTime64)
@@ -106,7 +100,6 @@ import Streamly.Internal.Data.Time.Units
106import Streamly.Internal.Data.Unfold.Types (Unfold(..))
100import Streamly.Internal.Data.Unfold.Types (Unfold(..))
107
101
108import qualified Streamly.Internal.Data.IORef.Prim as Prim
102import qualified Streamly.Internal.Data.IORef.Prim as Prim
109−import qualified Streamly.Internal.Data.Stream.StreamK as K
110
103
111import Prelude hiding (iterate, repeat, replicate, takeWhile)
104import Prelude hiding (iterate, repeat, replicate, takeWhile)
112import Streamly.Internal.Data.Stream.StreamD.Type
105import Streamly.Internal.Data.Stream.StreamD.Type
@@ -461,31 +454,3 @@ fromListM = Stream step
461 {-# INLINE_LATE step #-}
454 {-# INLINE_LATE step #-}
462 step _ (m:ms) = m >>= \x -> return $ Yield x ms
455 step _ (m:ms) = m >>= \x -> return $ Yield x ms
463 step _ [] = return Stop
456 step _ [] = return Stop
464−
465−-------------------------------------------------------------------------------
466−-- From callback
467−-------------------------------------------------------------------------------
468−
469−-- Note: we can use another API with two callbacks stop and yield if we want
470−-- the callback to be able to indicate end of stream.
471−--
472−-- | Generates a callback and a stream pair. The callback returned is used to
473−-- queue values to the stream. The stream is infinite, there is no way for the
474−-- callback to indicate that it is done now.
475−--
476−-- /Internal/
477−--
478−{-# INLINE_NORMAL newCallbackStream #-}
479−newCallbackStream :: (K.IsStream t, MonadAsync m) => m ((a -> m ()), t m a)
480−newCallbackStream = do
481− sv <- newParallelVar StopNone defState
482−
483− -- XXX Add our own thread-id to the SVar as we can not know the callback's
484− -- thread-id and the callback is not run in a managed worker. We need to
485− -- handle this better.
486− liftIO myThreadId >>= modifyThread sv
487−
488− let callback a = liftIO $ void $ send sv (ChildYield a)
489− -- XXX we can return an SVar and then the consumer can unfold from the
490− -- SVar?
491− return (callback, fromStreamD (fromSVar sv))
src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+2−117
@@ -27,7 +27,6 @@ module Streamly.Internal.Data.Stream.StreamD.Transform
27 -- * Mapping Effects
27 -- * Mapping Effects
28 , tap
28 , tap
29 , tapOffsetEvery
29 , tapOffsetEvery
30− , tapAsync
31 , tapRate
30 , tapRate
32 , pollCounts
31 , pollCounts
33
32
@@ -117,17 +116,12 @@ module Streamly.Internal.Data.Stream.StreamD.Transform
117 -- * Maybe Streams
116 -- * Maybe Streams
118 , mapMaybe
117 , mapMaybe
119 , mapMaybeM
118 , mapMaybeM
120−
121− -- * Threading
122− -- | Make the stream concurrent
123− , mkParallel
124− , mkParallelD
125 )
119 )
126where
120where
127
121
128#include "inline.hs"
122#include "inline.hs"
129
123
130−import Control.Concurrent (killThread, myThreadId, takeMVar, threadDelay)
124+import Control.Concurrent (killThread, threadDelay)
131import Control.Exception (AsyncException)
125import Control.Exception (AsyncException)
132import Control.Monad (void, when)
126import Control.Monad (void, when)
133import Control.Monad.Catch (MonadCatch, throwM)
127import Control.Monad.Catch (MonadCatch, throwM)
@@ -141,7 +135,6 @@ import qualified Control.Monad.Catch as MC
141
135
142import Streamly.Internal.Data.Fold.Types (Fold(..))
136import Streamly.Internal.Data.Fold.Types (Fold(..))
143import Streamly.Internal.Data.Pipe.Types (Pipe(..), PipeState(..))
137import Streamly.Internal.Data.Pipe.Types (Pipe(..), PipeState(..))
144−import Streamly.Internal.Data.Stream.SVar (fromConsumer, pushToFold)
145import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
138import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
146import Streamly.Internal.Data.Time.Units
139import Streamly.Internal.Data.Time.Units
147 (TimeUnit64, toRelTime64, diffAbsTime64)
140 (TimeUnit64, toRelTime64, diffAbsTime64)
@@ -354,7 +347,7 @@ pollCounts predicate transf fld (Stream step state) = Stream step' Nothing
354 countVar <- liftIO $ Prim.newIORef (0 :: Int)
347 countVar <- liftIO $ Prim.newIORef (0 :: Int)
355 tid <- forkManaged
348 tid <- forkManaged
356 $ void $ foldOnce fld
349 $ void $ foldOnce fld
357− $ transf $ fromPrimIORef countVar
350+ $ transf $ Prim.toStreamD countVar
358 return $ Skip (Just (countVar, tid, state))
351 return $ Skip (Just (countVar, tid, state))
359
352
360 step' gst (Just (countVar, tid, st)) = do
353 step' gst (Just (countVar, tid, st)) = do
@@ -412,75 +405,6 @@ tapRate samplingRate action (Stream step state) = Stream step' Nothing
412 liftIO $ killThread tid
405 liftIO $ killThread tid
413 return Stop
406 return Stop
414
407
415−-------------------------------------------------------------------------------
416−-- Concurrent tap
417−-------------------------------------------------------------------------------
418−
419−-- | Create an SVar with a fold consumer that will fold any elements sent to it
420−-- using the supplied fold function.
421−{-# INLINE newFoldSVar #-}
422−newFoldSVar :: MonadAsync m => State t m a -> Fold m a b -> m (SVar t m a)
423−newFoldSVar stt f = do
424− -- Buffer size for the SVar is derived from the current state
425− sv <- newParallelVar StopAny (adaptState stt)
426− -- Add the producer thread-id to the SVar.
427− liftIO myThreadId >>= modifyThread sv
428− void $ doFork (work sv) (svarMrun sv) (handleFoldException sv)
429− return sv
430−
431− where
432−
433− {-# NOINLINE work #-}
434− work sv = void $ foldOnce f $ fromProducer sv
435−
436−{-# INLINE_NORMAL tapAsync #-}
437−tapAsync :: MonadAsync m => Fold m a b -> Stream m a -> Stream m a
438−tapAsync f (Stream step1 state1) = Stream step TapInit
439− where
440−
441− drainFold svr = do
442− -- In general, a Stop event would come equipped with the result
443− -- of the fold. It is not used here but it would be useful in
444− -- applicative and distribute.
445− done <- fromConsumer svr
446− when (not done) $ do
447− liftIO $ withDiagMVar svr "teeToSVar: waiting to drain"
448− $ takeMVar (outputDoorBellFromConsumer svr)
449− drainFold svr
450−
451− stopFold svr = do
452− liftIO $ sendStop svr Nothing
453− -- drain/wait until a stop event arrives from the fold.
454− drainFold svr
455−
456− {-# INLINE_LATE step #-}
457− step gst TapInit = do
458− sv <- newFoldSVar gst f
459− return $ Skip (Tapping sv state1)
460−
461− step gst (Tapping sv st) = do
462− r <- step1 gst st
463− case r of
464− Yield a s -> do
465− done <- pushToFold sv a
466− if done
467− then do
468− -- XXX we do not need to wait synchronously here
469− stopFold sv
470− return $ Yield a (TapDone s)
471− else return $ Yield a (Tapping sv s)
472− Skip s -> return $ Skip (Tapping sv s)
473− Stop -> do
474− stopFold sv
475− return $ Stop
476−
477− step gst (TapDone st) = do
478− r <- step1 gst st
479− return $ case r of
480− Yield a s -> Yield a (TapDone s)
481− Skip s -> Skip (TapDone s)
482− Stop -> Stop
483−
484------------------------------------------------------------------------------
408------------------------------------------------------------------------------
485-- Scanning with a Fold
409-- Scanning with a Fold
486------------------------------------------------------------------------------
410------------------------------------------------------------------------------
@@ -1300,42 +1224,3 @@ mapMaybe f = fmap fromJust . filter isJust . map f
1300{-# INLINE_NORMAL mapMaybeM #-}
1224{-# INLINE_NORMAL mapMaybeM #-}
1301mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Stream m a -> Stream m b
1225mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Stream m a -> Stream m b
1302mapMaybeM f = fmap fromJust . filter isJust . mapM f
1226mapMaybeM f = fmap fromJust . filter isJust . mapM f
1303−
1304−-------------------------------------------------------------------------------
1305−-- Concurrent application and fold
1306−-------------------------------------------------------------------------------
1307−
1308−{-# INLINE_NORMAL mkParallelD #-}
1309−mkParallelD :: MonadAsync m => Stream m a -> Stream m a
1310−mkParallelD m = Stream step Nothing
1311− where
1312−
1313− step gst Nothing = do
1314− sv <- newParallelVar StopNone gst
1315− toSVarParallel gst sv m
1316− -- XXX use unfold instead?
1317− return $ Skip $ Just $ fromSVar sv
1318−
1319− step gst (Just (UnStream step1 st)) = do
1320− r <- step1 gst st
1321− return $ case r of
1322− Yield a s -> Yield a (Just $ Stream step1 s)
1323− Skip s -> Skip (Just $ Stream step1 s)
1324− Stop -> Stop
1325−
1326−-- Compare with mkAsync. mkAsync uses an Async style SVar whereas this uses a
1327−-- parallel style SVar for evaluation. Currently, parallel style cannot use
1328−-- rate control whereas Async style can use rate control. In async style SVar
1329−-- the worker thread terminates when the buffer is full whereas in Parallel
1330−-- style it blocks.
1331−--
1332−-- | Make the stream producer and consumer run concurrently by introducing a
1333−-- buffer between them. The producer thread evaluates the input stream until
1334−-- the buffer fills, it blocks if the buffer is full until there is space in
1335−-- the buffer. The consumer consumes the stream lazily from the buffer.
1336−--
1337−-- /Internal/
1338−--
1339−{-# INLINE_NORMAL mkParallel #-}
1340−mkParallel :: (K.IsStream t, MonadAsync m) => t m a -> t m a
1341−mkParallel = fromStreamD . mkParallelD . toStreamD
src/Streamly/Internal/Data/Stream/StreamD/Type.hs+3−214
@@ -32,9 +32,6 @@ module Streamly.Internal.Data.Stream.StreamD.Type
32
32
33 -- * From Containers
33 -- * From Containers
34 , fromList
34 , fromList
35− , fromProducer
36− , fromPrimIORef
37− , fromSVar
38
35
39 -- * Conversions From/To
36 -- * Conversions From/To
40 , fromStreamK
37 , fromStreamK
@@ -60,7 +57,6 @@ module Streamly.Internal.Data.Stream.StreamD.Type
60
57
61 -- * To Containers
58 -- * To Containers
62 , toList
59 , toList
63− , toSVarParallel
64
60
65 -- * Multi-stream folds
61 -- * Multi-stream folds
66 , eqBy
62 , eqBy
@@ -84,29 +80,20 @@ module Streamly.Internal.Data.Stream.StreamD.Type
84where
80where
85
81
86import Control.Applicative (liftA2)
82import Control.Applicative (liftA2)
87−import Control.Exception (fromException)
88import Control.Monad (when)
83import Control.Monad (when)
89import Control.Monad.Catch (MonadThrow, throwM)
84import Control.Monad.Catch (MonadThrow, throwM)
90−import Control.Monad.IO.Class (MonadIO(..))
91import Control.Monad.Trans.Class (lift, MonadTrans)
85import Control.Monad.Trans.Class (lift, MonadTrans)
92−import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
93import Data.Functor.Identity (Identity(..))
86import Data.Functor.Identity (Identity(..))
94−import Data.Maybe (isNothing)
95import Fusion.Plugin.Types (Fuse(..))
87import Fusion.Plugin.Types (Fuse(..))
96import GHC.Base (build)
88import GHC.Base (build)
97import GHC.Types (SPEC(..))
89import GHC.Types (SPEC(..))
98import Prelude hiding (map, mapM, foldr, take, concatMap, takeWhile)
90import Prelude hiding (map, mapM, foldr, take, concatMap, takeWhile)
99−import System.Mem (performMajorGC)
100
91
101−import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
102−import Streamly.Internal.Data.IORef.Prim (Prim)
103−import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
104import Streamly.Internal.Data.Fold.Types (Fold(..), Fold2(..))
92import Streamly.Internal.Data.Fold.Types (Fold(..), Fold2(..))
105−import Streamly.Internal.Data.SVar
93+import Streamly.Internal.Data.SVar (State, adaptState, defState)
106
94
107−import qualified Streamly.Internal.Data.Fold as FL
95+import qualified Streamly.Internal.Data.Fold.Types as FL
108−import qualified Streamly.Internal.Data.Stream.StreamK as K
96+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
109−import qualified Streamly.Internal.Data.IORef.Prim as Prim
110
97
111------------------------------------------------------------------------------
98------------------------------------------------------------------------------
112-- The direct style stream type
99-- The direct style stream type
@@ -213,132 +200,6 @@ fromList = Stream step
213 step _ (x:xs) = pure $ Yield x xs
200 step _ (x:xs) = pure $ Yield x xs
214 step _ [] = pure Stop
201 step _ [] = pure Stop
215
202
216−data FromSVarState t m a =
217− FromSVarInit
218− | FromSVarRead (SVar t m a)
219− | FromSVarLoop (SVar t m a) [ChildEvent a]
220− | FromSVarDone (SVar t m a)
221−
222−{-# INLINE_NORMAL fromSVar #-}
223−fromSVar :: (MonadAsync m) => SVar t m a -> Stream m a
224−fromSVar svar = Stream step FromSVarInit
225− where
226−
227− {-# INLINE_LATE step #-}
228− step _ FromSVarInit = do
229− ref <- liftIO $ newIORef ()
230− _ <- liftIO $ mkWeakIORef ref hook
231− -- when this copy of svar gets garbage collected "ref" will get
232− -- garbage collected and our GC hook will be called.
233− let sv = svar{svarRef = Just ref}
234− return $ Skip (FromSVarRead sv)
235−
236− where
237−
238− {-# NOINLINE hook #-}
239− hook = do
240− when (svarInspectMode svar) $ do
241− r <- liftIO $ readIORef (svarStopTime (svarStats svar))
242− when (isNothing r) $
243− printSVar svar "SVar Garbage Collected"
244− cleanupSVar svar
245− -- If there are any SVars referenced by this SVar a GC will prompt
246− -- them to be cleaned up quickly.
247− when (svarInspectMode svar) performMajorGC
248−
249− step _ (FromSVarRead sv) = do
250− list <- readOutputQ sv
251− -- Reversing the output is important to guarantee that we process the
252− -- outputs in the same order as they were generated by the constituent
253− -- streams.
254− return $ Skip $ FromSVarLoop sv (Prelude.reverse list)
255−
256− step _ (FromSVarLoop sv []) = do
257− done <- postProcess sv
258− return $ Skip $ if done
259− then (FromSVarDone sv)
260− else (FromSVarRead sv)
261−
262− step _ (FromSVarLoop sv (ev : es)) = do
263− case ev of
264− ChildYield a -> return $ Yield a (FromSVarLoop sv es)
265− ChildStop tid e -> do
266− accountThread sv tid
267− case e of
268− Nothing -> do
269− stop <- shouldStop tid
270− if stop
271− then do
272− liftIO (cleanupSVar sv)
273− return $ Skip (FromSVarDone sv)
274− else return $ Skip (FromSVarLoop sv es)
275− Just ex ->
276− case fromException ex of
277− Just ThreadAbort ->
278− return $ Skip (FromSVarLoop sv es)
279− Nothing -> liftIO (cleanupSVar sv) >> throwM ex
280− where
281−
282− shouldStop tid =
283− case svarStopStyle sv of
284− StopNone -> return False
285− StopAny -> return True
286− StopBy -> do
287− sid <- liftIO $ readIORef (svarStopBy sv)
288− return $ if tid == sid then True else False
289−
290− step _ (FromSVarDone sv) = do
291− when (svarInspectMode sv) $ do
292− t <- liftIO $ getTime Monotonic
293− liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
294− liftIO $ printSVar sv "SVar Done"
295− return Stop
296−
297−{-# INLINE_NORMAL fromPrimIORef #-}
298−fromPrimIORef :: (MonadIO m, Prim a) => Prim.IORef a -> Stream m a
299−fromPrimIORef var = Stream step ()
300− where
301− {-# INLINE_LATE step #-}
302− step _ () = liftIO (Prim.readIORef var) >>= \x -> return $ Yield x ()
303−
304−-------------------------------------------------------------------------------
305−-- Process events received by a fold consumer from a stream producer
306−-------------------------------------------------------------------------------
307−
308−{-# INLINE_NORMAL fromProducer #-}
309−fromProducer :: (MonadAsync m) => SVar t m a -> Stream m a
310−fromProducer svar = Stream step (FromSVarRead svar)
311− where
312−
313− {-# INLINE_LATE step #-}
314− step _ (FromSVarRead sv) = do
315− list <- readOutputQ sv
316− -- Reversing the output is important to guarantee that we process the
317− -- outputs in the same order as they were generated by the constituent
318− -- streams.
319− return $ Skip $ FromSVarLoop sv (Prelude.reverse list)
320−
321− step _ (FromSVarLoop sv []) = return $ Skip $ FromSVarRead sv
322− step _ (FromSVarLoop sv (ev : es)) = do
323− case ev of
324− ChildYield a -> return $ Yield a (FromSVarLoop sv es)
325− ChildStop tid e -> do
326− accountThread sv tid
327− case e of
328− Nothing -> do
329− sendStopToProducer sv
330− return $ Skip (FromSVarDone sv)
331− Just _ -> error "Bug: fromProducer: received exception"
332−
333− step _ (FromSVarDone sv) = do
334− when (svarInspectMode sv) $ do
335− t <- liftIO $ getTime Monotonic
336− liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
337− liftIO $ printSVar sv "SVar Done"
338− return Stop
339−
340− step _ FromSVarInit = undefined
341−
342------------------------------------------------------------------------------
203------------------------------------------------------------------------------
343-- Conversions From/To
204-- Conversions From/To
344------------------------------------------------------------------------------
205------------------------------------------------------------------------------
@@ -576,78 +437,6 @@ toListFB c n (Stream step state) = go state
576toListId :: Stream Identity a -> Identity [a]
437toListId :: Stream Identity a -> Identity [a]
577toListId s = Identity $ build (\c n -> toListFB c n s)
438toListId s = Identity $ build (\c n -> toListFB c n s)
578
439
579−-------------------------------------------------------------------------------
580−-- Concurrent application and fold
581−-------------------------------------------------------------------------------
582−
583−-- XXX These functions should be moved to Stream/Parallel.hs
584−--
585−-- Using StreamD the worker stream producing code can fuse with the code to
586−-- queue output to the SVar giving some perf boost.
587−--
588−-- Note that StreamD can only be used in limited situations, specifically, we
589−-- cannot implement joinStreamVarPar using this.
590−--
591−-- XXX make sure that the SVar passed is a Parallel style SVar.
592−
593−-- | Fold the supplied stream to the SVar asynchronously using Parallel
594−-- concurrency style.
595−-- {-# INLINE_NORMAL toSVarParallel #-}
596−{-# INLINE toSVarParallel #-}
597−toSVarParallel :: MonadAsync m
598− => State t m a -> SVar t m a -> Stream m a -> m ()
599−toSVarParallel st sv xs =
600− if svarInspectMode sv
601− then forkWithDiag
602− else do
603− tid <-
604− case getYieldLimit st of
605− Nothing -> doFork (work Nothing)
606− (svarMrun sv)
607− (handleChildException sv)
608− Just _ -> doFork (workLim Nothing)
609− (svarMrun sv)
610− (handleChildException sv)
611− modifyThread sv tid
612−
613− where
614−
615− {-# NOINLINE work #-}
616− work info = (foldOnce (FL.toParallelSVar sv info) xs)
617−
618− {-# NOINLINE workLim #-}
619− workLim info = foldOnce (FL.toParallelSVarLimited sv info) xs
620−
621− {-# NOINLINE forkWithDiag #-}
622− forkWithDiag = do
623− -- We do not use workerCount in case of ParallelVar but still there is
624− -- no harm in maintaining it correctly.
625− liftIO $ atomicModifyIORefCAS_ (workerCount sv) $ \n -> n + 1
626− recordMaxWorkers sv
627− -- This allocation matters when significant number of workers are being
628− -- sent. We allocate it only when needed. The overhead increases by 4x.
629− winfo <-
630− case yieldRateInfo sv of
631− Nothing -> return Nothing
632− Just _ -> liftIO $ do
633− cntRef <- newIORef 0
634− t <- getTime Monotonic
635− lat <- newIORef (0, t)
636− return $ Just WorkerInfo
637− { workerYieldMax = 0
638− , workerYieldCount = cntRef
639− , workerLatencyStart = lat
640− }
641− tid <-
642− case getYieldLimit st of
643− Nothing -> doFork (work winfo)
644− (svarMrun sv)
645− (handleChildException sv)
646− Just _ -> doFork (workLim winfo)
647− (svarMrun sv)
648− (handleChildException sv)
649− modifyThread sv tid
650−
651------------------------------------------------------------------------------
440------------------------------------------------------------------------------
652-- Multi-stream folds
441-- Multi-stream folds
653------------------------------------------------------------------------------
442------------------------------------------------------------------------------
src/Streamly/Internal/Data/Stream/Zip.hs+10−6
@@ -53,13 +53,17 @@ import Streamly.Internal.Data.Stream.StreamK (IsStream(..), Stream)
53import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
53import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
54import Streamly.Internal.Data.SVar (MonadAsync)
54import Streamly.Internal.Data.SVar (MonadAsync)
55
55
56+import qualified Streamly.Internal.Data.Stream.Parallel as Par
56import qualified Streamly.Internal.Data.Stream.Prelude as P
57import qualified Streamly.Internal.Data.Stream.Prelude as P
58+ (cmpBy, eqBy, foldl', foldr, fromList, toList, fromStreamS, toStreamS)
57−import qualified Streamly.Internal.Data.Stream.StreamK as K
59+import qualified Streamly.Internal.Data.Stream.StreamK as K (repeat)
58−import qualified Streamly.Internal.Data.Stream.StreamD as D
60+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
61+import qualified Streamly.Internal.Data.Stream.StreamD as D (zipWithM)
62+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
59#ifdef USE_STREAMK_ONLY
63#ifdef USE_STREAMK_ONLY
60−import qualified Streamly.Internal.Data.Stream.StreamK as S
64+import qualified Streamly.Internal.Data.Stream.StreamK as S (zipWith, zipWithM)
61#else
65#else
62−import qualified Streamly.Internal.Data.Stream.StreamD as S
66+import qualified Streamly.Internal.Data.Stream.StreamD as S (zipWith, zipWithM)
63#endif
67#endif
64
68
65import Prelude hiding (map, repeat, zipWith, errorWithoutStackTrace)
69import Prelude hiding (map, repeat, zipWith, errorWithoutStackTrace)
@@ -97,8 +101,8 @@ zipWith f m1 m2 = P.fromStreamS $ S.zipWith f (P.toStreamS m1) (P.toStreamS m2)
97zipAsyncWithM :: (IsStream t, MonadAsync m)
101zipAsyncWithM :: (IsStream t, MonadAsync m)
98 => (a -> b -> m c) -> t m a -> t m b -> t m c
102 => (a -> b -> m c) -> t m a -> t m b -> t m c
99zipAsyncWithM f m1 m2 = D.fromStreamD $
103zipAsyncWithM f m1 m2 = D.fromStreamD $
100− D.zipWithM f (D.mkParallelD $ D.toStreamD m1)
104+ D.zipWithM f (Par.mkParallelD $ D.toStreamD m1)
101− (D.mkParallelD $ D.toStreamD m2)
105+ (Par.mkParallelD $ D.toStreamD m2)
102
106
103-- | Like 'zipWith' but zips concurrently i.e. both the streams being zipped
107-- | Like 'zipWith' but zips concurrently i.e. both the streams being zipped
104-- are generated concurrently.
108-- are generated concurrently.
src/Streamly/Internal/Data/Unfold.hs+3−1
@@ -164,8 +164,10 @@ import qualified Prelude
164import qualified Control.Monad.Catch as MC
164import qualified Control.Monad.Catch as MC
165import qualified Data.Tuple as Tuple
165import qualified Data.Tuple as Tuple
166import qualified Streamly.Internal.Data.Fold.Types as FL
166import qualified Streamly.Internal.Data.Fold.Types as FL
167−import qualified Streamly.Internal.Data.Stream.StreamK as K
167+import qualified Streamly.Internal.Data.Stream.StreamK as K (uncons)
168+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
168import qualified Streamly.Internal.Data.Stream.StreamD as D
169import qualified Streamly.Internal.Data.Stream.StreamD as D
170+ (unfold, newFinalizedIORef, runIORefFinalizer, withIORefFinalizer)
169
171
170import Streamly.Internal.Data.SVar
172import Streamly.Internal.Data.SVar
171import Prelude
173import Prelude
streamly.cabal+5−3
@@ -418,7 +418,6 @@ library
418 , Streamly.Internal.Data.Tuple.Strict
418 , Streamly.Internal.Data.Tuple.Strict
419 , Streamly.Internal.Data.Maybe.Strict
419 , Streamly.Internal.Data.Maybe.Strict
420 , Streamly.Internal.Data.Either.Strict
420 , Streamly.Internal.Data.Either.Strict
421− , Streamly.Internal.Data.IORef.Prim
422 , Streamly.Internal.Foreign.Malloc
421 , Streamly.Internal.Foreign.Malloc
423 , Streamly.Internal.Data.Atomics
422 , Streamly.Internal.Data.Atomics
424 , Streamly.Internal.Data.Time
423 , Streamly.Internal.Data.Time
@@ -429,15 +428,18 @@ library
429 -- streamly-core-stream
428 -- streamly-core-stream
430 , Streamly.Internal.Data.SVar
429 , Streamly.Internal.Data.SVar
431 , Streamly.Internal.Data.Stream.StreamK.Type
430 , Streamly.Internal.Data.Stream.StreamK.Type
431+ , Streamly.Internal.Data.Fold.Types
432 , Streamly.Internal.Data.Stream.StreamD.Type
432 , Streamly.Internal.Data.Stream.StreamD.Type
433 , Streamly.Internal.Data.Stream.StreamDK.Type
433 , Streamly.Internal.Data.Stream.StreamDK.Type
434 , Streamly.Internal.Data.Unfold.Types
434 , Streamly.Internal.Data.Unfold.Types
435− , Streamly.Internal.Data.Fold.Types
436 , Streamly.Internal.Data.Sink.Types
435 , Streamly.Internal.Data.Sink.Types
437 , Streamly.Internal.Data.Parser.ParserK.Types
436 , Streamly.Internal.Data.Parser.ParserK.Types
438 , Streamly.Internal.Data.Parser.ParserD.Types
437 , Streamly.Internal.Data.Parser.ParserD.Types
439 , Streamly.Internal.Data.Pipe.Types
438 , Streamly.Internal.Data.Pipe.Types
440
439
440+ -- Unboxed IORef
441+ , Streamly.Internal.Data.IORef.Prim
442+
441 -- streamly-core-array
443 -- streamly-core-array
442 -- May depend on streamly-core-stream
444 -- May depend on streamly-core-stream
443 , Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types
445 , Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types
@@ -477,13 +479,13 @@ library
477 , Streamly.Internal.Data.Stream.Parallel
479 , Streamly.Internal.Data.Stream.Parallel
478 , Streamly.Internal.Data.Stream.Ahead
480 , Streamly.Internal.Data.Stream.Ahead
479 , Streamly.Internal.Data.Stream.Zip
481 , Streamly.Internal.Data.Stream.Zip
482+ , Streamly.Internal.Data.Stream.IsStream.Combinators
480 , Streamly.Internal.Data.Stream.IsStream.Common
483 , Streamly.Internal.Data.Stream.IsStream.Common
481 , Streamly.Internal.Data.Stream.IsStream.Types
484 , Streamly.Internal.Data.Stream.IsStream.Types
482 , Streamly.Internal.Data.Stream.IsStream.Enumeration
485 , Streamly.Internal.Data.Stream.IsStream.Enumeration
483 , Streamly.Internal.Data.Stream.IsStream.Generate
486 , Streamly.Internal.Data.Stream.IsStream.Generate
484 , Streamly.Internal.Data.Stream.IsStream.Eliminate
487 , Streamly.Internal.Data.Stream.IsStream.Eliminate
485 , Streamly.Internal.Data.Stream.IsStream.Transform
488 , Streamly.Internal.Data.Stream.IsStream.Transform
486− , Streamly.Internal.Data.Stream.IsStream.Combinators
487 , Streamly.Internal.Data.Stream.IsStream.Nesting
489 , Streamly.Internal.Data.Stream.IsStream.Nesting
488 , Streamly.Internal.Data.Stream.IsStream.Exception
490 , Streamly.Internal.Data.Stream.IsStream.Exception
489 , Streamly.Internal.Data.Stream.IsStream.Lift
491 , Streamly.Internal.Data.Stream.IsStream.Lift