on 6f4fd8d help sign in
232 lines 8.8 kB history raw
1#include "inline.hs"
3-- |
4-- Module : Streamly.Internal.Data.Stream.Combinators
5-- Copyright : (c) 2017 Harendra Kumar
6--
7-- License : BSD3
8-- Maintainer : streamly@composewell.com
9-- Stability : experimental
10-- Portability : GHC
11--
12--
13module Streamly.Internal.Data.Stream.Combinators
14 ( maxThreads
15 , maxBuffer
16 , maxYields
17 , rate
18 , avgRate
19 , minRate
20 , maxRate
21 , constRate
22 , inspectMode
23 , printState
24 )
25where
27import Control.Monad.IO.Class (MonadIO(liftIO))
28import Data.Int (Int64)
30import Streamly.Internal.Data.SVar
31import Streamly.Internal.Data.Stream.StreamK
32import Streamly.Internal.Data.Stream.Serial (SerialT)
34-------------------------------------------------------------------------------
35-- Concurrency control
36-------------------------------------------------------------------------------
37--
38-- XXX need to write these in direct style otherwise they will break fusion.
39--
40-- | Specify the maximum number of threads that can be spawned concurrently for
41-- any concurrent combinator in a stream.
42-- A value of 0 resets the thread limit to default, a negative value means
43-- there is no limit. The default value is 1500. 'maxThreads' does not affect
44-- 'ParallelT' streams as they can use unbounded number of threads.
45--
46-- When the actions in a stream are IO bound, having blocking IO calls, this
47-- option can be used to control the maximum number of in-flight IO requests.
48-- When the actions are CPU bound this option can be used to
49-- control the amount of CPU used by the stream.
50--
51-- /Since: 0.4.0 ("Streamly")/
52--
53-- @since 0.8.0
54{-# INLINE_NORMAL maxThreads #-}
55maxThreads :: IsStream t => Int -> t m a -> t m a
56maxThreads n m = mkStream $ \st stp sng yld ->
57 foldStreamShared (setMaxThreads n st) stp sng yld m
59{-
60{-# RULES "maxThreadsSerial serial" maxThreads = maxThreadsSerial #-}
61maxThreadsSerial :: Int -> SerialT m a -> SerialT m a
62maxThreadsSerial _ = id
63-}
65-- | Specify the maximum size of the buffer for storing the results from
66-- concurrent computations. If the buffer becomes full we stop spawning more
67-- concurrent tasks until there is space in the buffer.
68-- A value of 0 resets the buffer size to default, a negative value means
69-- there is no limit. The default value is 1500.
70--
71-- CAUTION! using an unbounded 'maxBuffer' value (i.e. a negative value)
72-- coupled with an unbounded 'maxThreads' value is a recipe for disaster in
73-- presence of infinite streams, or very large streams. Especially, it must
74-- not be used when 'pure' is used in 'ZipAsyncM' streams as 'pure' in
75-- applicative zip streams generates an infinite stream causing unbounded
76-- concurrent generation with no limit on the buffer or threads.
77--
78-- /Since: 0.4.0 ("Streamly")/
79--
80-- @since 0.8.0
81{-# INLINE_NORMAL maxBuffer #-}
82maxBuffer :: IsStream t => Int -> t m a -> t m a
83maxBuffer n m = mkStream $ \st stp sng yld ->
84 foldStreamShared (setMaxBuffer n st) stp sng yld m
86{-
87{-# RULES "maxBuffer serial" maxBuffer = maxBufferSerial #-}
88maxBufferSerial :: Int -> SerialT m a -> SerialT m a
89maxBufferSerial _ = id
90-}
92-- | Specify the pull rate of a stream.
93-- A 'Nothing' value resets the rate to default which is unlimited. When the
94-- rate is specified, concurrent production may be ramped up or down
95-- automatically to achieve the specified yield rate. The specific behavior for
96-- different styles of 'Rate' specifications is documented under 'Rate'. The
97-- effective maximum production rate achieved by a stream is governed by:
98--
99-- * The 'maxThreads' limit
100-- * The 'maxBuffer' limit
101-- * The maximum rate that the stream producer can achieve
102-- * The maximum rate that the stream consumer can achieve
103--
104-- /Since: 0.5.0 ("Streamly")/
105--
106-- @since 0.8.0
107{-# INLINE_NORMAL rate #-}
108rate :: IsStream t => Maybe Rate -> t m a -> t m a
109rate r m = mkStream $ \st stp sng yld ->
110 case r of
111 Just (Rate low goal _ _) | goal < low ->
112 error "rate: Target rate cannot be lower than minimum rate."
113 Just (Rate _ goal high _) | goal > high ->
114 error "rate: Target rate cannot be greater than maximum rate."
115 Just (Rate low _ high _) | low > high ->
116 error "rate: Minimum rate cannot be greater than maximum rate."
117 _ -> foldStreamShared (setStreamRate r st) stp sng yld m
119-- XXX implement for serial streams as well, as a simple delay
121{-
122{-# RULES "rate serial" rate = yieldRateSerial #-}
123yieldRateSerial :: Double -> SerialT m a -> SerialT m a
124yieldRateSerial _ = id
125-}
127-- | Same as @rate (Just $ Rate (r/2) r (2*r) maxBound)@
128--
129-- Specifies the average production rate of a stream in number of yields
130-- per second (i.e. @Hertz@). Concurrent production is ramped up or down
131-- automatically to achieve the specified average yield rate. The rate can
132-- go down to half of the specified rate on the lower side and double of
133-- the specified rate on the higher side.
134--
135-- /Since: 0.5.0 ("Streamly")/
136--
137-- @since 0.8.0
138avgRate :: IsStream t => Double -> t m a -> t m a
139avgRate r = rate (Just $ Rate (r/2) r (2*r) maxBound)
141-- | Same as @rate (Just $ Rate r r (2*r) maxBound)@
142--
143-- Specifies the minimum rate at which the stream should yield values. As
144-- far as possible the yield rate would never be allowed to go below the
145-- specified rate, even though it may possibly go above it at times, the
146-- upper limit is double of the specified rate.
147--
148-- /Since: 0.5.0 ("Streamly")/
149--
150-- @since 0.8.0
151minRate :: IsStream t => Double -> t m a -> t m a
152minRate r = rate (Just $ Rate r r (2*r) maxBound)
154-- | Same as @rate (Just $ Rate (r/2) r r maxBound)@
155--
156-- Specifies the maximum rate at which the stream should yield values. As
157-- far as possible the yield rate would never be allowed to go above the
158-- specified rate, even though it may possibly go below it at times, the
159-- lower limit is half of the specified rate. This can be useful in
160-- applications where certain resource usage must not be allowed to go
161-- beyond certain limits.
162--
163-- /Since: 0.5.0 ("Streamly")/
164--
165-- @since 0.8.0
166maxRate :: IsStream t => Double -> t m a -> t m a
167maxRate r = rate (Just $ Rate (r/2) r r maxBound)
169-- | Same as @rate (Just $ Rate r r r 0)@
170--
171-- Specifies a constant yield rate. If for some reason the actual rate
172-- goes above or below the specified rate we do not try to recover it by
173-- increasing or decreasing the rate in future. This can be useful in
174-- applications like graphics frame refresh where we need to maintain a
175-- constant refresh rate.
176--
177-- /Since: 0.5.0 ("Streamly")/
178--
179-- @since 0.8.0
180constRate :: IsStream t => Double -> t m a -> t m a
181constRate r = rate (Just $ Rate r r r 0)
183-- | Specify the average latency, in nanoseconds, of a single threaded action
184-- in a concurrent composition. Streamly can measure the latencies, but that is
185-- possible only after at least one task has completed. This combinator can be
186-- used to provide a latency hint so that rate control using 'rate' can take
187-- that into account right from the beginning. When not specified then a
188-- default behavior is chosen which could be too slow or too fast, and would be
189-- restricted by any other control parameters configured.
190-- A value of 0 indicates default behavior, a negative value means there is no
191-- limit i.e. zero latency.
192-- This would normally be useful only in high latency and high throughput
193-- cases.
194--
195{-# INLINE_NORMAL _serialLatency #-}
196_serialLatency :: IsStream t => Int -> t m a -> t m a
197_serialLatency n m = mkStream $ \st stp sng yld ->
198 foldStreamShared (setStreamLatency n st) stp sng yld m
200{-
201{-# RULES "serialLatency serial" _serialLatency = serialLatencySerial #-}
202serialLatencySerial :: Int -> SerialT m a -> SerialT m a
203serialLatencySerial _ = id
204-}
206-- Stop concurrent dispatches after this limit. This is useful in API's like
207-- "take" where we want to dispatch only upto the number of elements "take"
208-- needs. This value applies only to the immediate next level and is not
209-- inherited by everything in enclosed scope.
210{-# INLINE_NORMAL maxYields #-}
211maxYields :: IsStream t => Maybe Int64 -> t m a -> t m a
212maxYields n m = mkStream $ \st stp sng yld ->
213 foldStreamShared (setYieldLimit n st) stp sng yld m
215{-# RULES "maxYields serial" maxYields = maxYieldsSerial #-}
216maxYieldsSerial :: Maybe Int64 -> SerialT m a -> SerialT m a
217maxYieldsSerial _ = id
219printState :: MonadIO m => State Stream m a -> m ()
220printState st = liftIO $ do
221 let msv = streamVar st
222 case msv of
223 Just sv -> dumpSVar sv >>= putStrLn
224 Nothing -> putStrLn "No SVar"
226-- | Print debug information about an SVar when the stream ends
227--
228-- /Internal/
229--
230inspectMode :: IsStream t => t m a -> t m a
231inspectMode m = mkStream $ \st stp sng yld ->
232 foldStreamShared (setInspectMode st) stp sng yld m