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