2-- Module : Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
9module Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
17import Control.Concurrent (myThreadId)
18import Control.Concurrent.MVar (newEmptyMVar)
19import Control.Monad.IO.Class (MonadIO(liftIO))
20import Control.Monad.Trans.Control (MonadBaseControl, restoreM)
21import Data.Concurrent.Queue.MichaelScott (LinkedQueue, newQ, nullQ, tryPopR, pushL)
22import Data.IORef (newIORef, readIORef)
23import Streamly.Internal.Control.Concurrent
24 (MonadRunInIO, MonadAsync, RunInIO(..), askRunInIO)
25import Streamly.Internal.Data.Stream.Channel.Dispatcher (delThread)
27import qualified Data.Set as Set
28import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
30import Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
31import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
32import Streamly.Internal.Data.Stream.Channel.Types
34------------------------------------------------------------------------------
36------------------------------------------------------------------------------
38data WorkerStatus = Continue | Suspend
40-- XXX This is not strictly round-robin as the streams that are faster may
41-- yield more elements than the ones that are slower. Also, when streams
42-- suspend due to buffer getting full they get added to the queue in a random
45{-# INLINE enqueueFIFO #-}
48 -> LinkedQueue (RunInIO m, K.Stream m a)
49 -> (RunInIO m, K.Stream m a)
51enqueueFIFO sv q m = do
53 ringDoorBell (doorBellOnWorkQ sv) (outputDoorBell sv)
55{-# INLINE workLoopFIFO #-}
58 => LinkedQueue (RunInIO m, K.Stream m a)
62workLoopFIFO q sv winfo = run
67 work <- liftIO $ tryPopR q
69 Nothing -> liftIO $ stop sv winfo
70 Just (RunInIO runin, m) -> do
74 undefined yieldk single (return Continue) m
78 Suspend -> liftIO $ stop sv winfo
81 res <- liftIO $ yield sv winfo a
82 return $ if res then Continue else Suspend
84 -- XXX in general we would like to yield "n" elements from a single stream
85 -- before moving on to the next. Single element granularity could be too
86 -- expensive in certain cases. Similarly, we can use time limit for
89 res <- liftIO $ yield sv winfo a
91 liftIO $ enqueueFIFO sv q (runInIO, r)
92 return $ if res then Continue else Suspend
94{-# INLINE workLoopFIFOLimited #-}
96 :: forall m a. MonadRunInIO m
97 => LinkedQueue (RunInIO m, K.Stream m a)
101workLoopFIFOLimited q sv winfo = run
106 liftIO (incrementYieldLimit (remainingWork sv)) >> return Continue
109 work <- liftIO $ tryPopR q
111 Nothing -> liftIO $ stop sv winfo
112 Just (RunInIO runin, m) -> do
113 yieldLimitOk <- liftIO $ decrementYieldLimit (remainingWork sv)
119 undefined yieldk single incrContinue m
123 Suspend -> liftIO $ stop sv winfo
125 enqueueFIFO sv q (RunInIO runin, m)
126 incrementYieldLimit (remainingWork sv)
130 res <- liftIO $ yield sv winfo a
131 return $ if res then Continue else Suspend
134 res <- liftIO $ yield sv winfo a
135 runInIO <- askRunInIO
136 liftIO $ enqueueFIFO sv q (runInIO, r)
137 yieldLimitOk <- liftIO $ decrementYieldLimit (remainingWork sv)
138 if res && yieldLimitOk
141 incrementYieldLimit (remainingWork sv)
144-------------------------------------------------------------------------------
146-------------------------------------------------------------------------------
148-- XXX we have this function in this file because passing runStreamLIFO as a
149-- function argument to this function results in a perf degradation of more
150-- than 10%. Need to investigate what the root cause is.
151-- Interestingly, the same thing does not make any difference for Ahead.
152getFifoSVar :: forall m a. (MonadIO m, MonadBaseControl IO m) =>
153 RunInIO m -> Config -> IO (Channel m a)
154getFifoSVar mrun cfg = do
155 outQ <- newIORef ([], 0)
156 outQMv <- newEmptyMVar
158 wfw <- newIORef False
159 running <- newIORef Set.empty
161 yl <- case getYieldLimit cfg of
162 Nothing -> return Nothing
163 Just x -> Just <$> newIORef x
164 rateInfo <- newRateInfo cfg
166 stats <- newSVarStats
169 let isWorkFinished _ = nullQ q
170 let isWorkFinishedLimited sv = do
172 case remainingWork sv of
176 Nothing -> return False
178 return $ qEmpty || yieldsDone
180 let getSVar :: Channel m a
181 -> (Channel m a -> m [ChildEvent a])
182 -> (Channel m a -> m Bool)
183 -> (Channel m a -> IO Bool)
184 -> (LinkedQueue (RunInIO m, K.Stream m a)
189 getSVar sv readOutput postProc workDone wloop = Channel
192 , maxBufferLimit = getMaxBuffer cfg
193 , maxWorkerLimit = min (getMaxThreads cfg) (getMaxBuffer cfg)
194 , yieldRateInfo = rateInfo
195 , outputDoorBell = outQMv
196 , readOutputQ = readOutput sv
197 , postProcess = postProc sv
198 , workerThreads = running
199 , workLoop = wloop q sv
200 , enqueue = \_ -> enqueueFIFO sv q
201 , eagerDispatch = return ()
202 , isWorkDone = workDone sv
203 , isQueueDone = workDone sv
204 , doorBellOnWorkQ = wfw
206 , workerCount = active
207 , accountThread = delThread running
208 , workerStopMVar = undefined
210 , svarInspectMode = getInspectMode cfg
216 case getStreamRate cfg of
218 case getYieldLimit cfg of
219 Nothing -> getSVar sv (readOutputQBounded False)
223 Just _ -> getSVar sv (readOutputQBounded False)
225 isWorkFinishedLimited
228 case getYieldLimit cfg of
229 Nothing -> getSVar sv readOutputQPaced
233 Just _ -> getSVar sv readOutputQPaced
235 isWorkFinishedLimited
239-- XXX GHC: If instead of MonadAsync we use (MonadIO m, MonadBaseControl IO m)
240-- constraint we get a 2x perf regression. Need to look into that.
242-- | Create a new async style concurrent stream evaluation channel. The monad
243-- state used to run the stream actions is taken from the call site of
245{-# INLINABLE newChannel #-}
246{-# SPECIALIZE newChannel :: (Config -> Config) -> IO (Channel IO a) #-}
247newChannel :: MonadAsync m =>
248 (Config -> Config) -> m (Channel m a)
249newChannel modifier = do
251 liftIO $ getFifoSVar mrun (modifier defaultConfig)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list