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 Data.Concurrent.Queue.MichaelScott (LinkedQueue, newQ, nullQ, tryPopR, pushL)
21import Data.IORef (newIORef, readIORef)
22import Streamly.Internal.Control.Concurrent
23 (MonadRunInIO, MonadAsync, RunInIO(..), askRunInIO, restoreM)
24import Streamly.Internal.Data.Stream.Channel.Dispatcher (delThread)
26import qualified Data.Set as Set
27import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
29import Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
30import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
31import Streamly.Internal.Data.Stream.Channel.Types
33------------------------------------------------------------------------------
35------------------------------------------------------------------------------
37data WorkerStatus = Continue | Suspend
39-- XXX This is not strictly round-robin as the streams that are faster may
40-- yield more elements than the ones that are slower. Also, when streams
41-- suspend due to buffer getting full they get added to the queue in a random
44{-# INLINE enqueueFIFO #-}
47 -> LinkedQueue (RunInIO m, K.StreamK m a)
48 -> (RunInIO m, K.StreamK m a)
50enqueueFIFO sv q m = do
52 ringDoorBell (doorBellOnWorkQ sv) (outputDoorBell sv)
54{-# INLINE workLoopFIFO #-}
57 => LinkedQueue (RunInIO m, K.StreamK m a)
61workLoopFIFO q sv winfo = run
66 work <- liftIO $ tryPopR q
68 Nothing -> liftIO $ stop sv winfo
69 Just (RunInIO runin, m) -> do
73 undefined yieldk single (return Continue) m
77 Suspend -> liftIO $ stop sv winfo
80 res <- liftIO $ yield sv winfo a
81 return $ if res then Continue else Suspend
83 -- XXX in general we would like to yield "n" elements from a single stream
84 -- before moving on to the next. Single element granularity could be too
85 -- expensive in certain cases. Similarly, we can use time limit for
88 res <- liftIO $ yield sv winfo a
90 liftIO $ enqueueFIFO sv q (runInIO, r)
91 return $ if res then Continue else Suspend
93{-# INLINE workLoopFIFOLimited #-}
95 :: forall m a. MonadRunInIO m
96 => LinkedQueue (RunInIO m, K.StreamK m a)
100workLoopFIFOLimited q sv winfo = run
105 liftIO (incrementYieldLimit (remainingWork sv)) >> return Continue
108 work <- liftIO $ tryPopR q
110 Nothing -> liftIO $ stop sv winfo
111 Just (RunInIO runin, m) -> do
112 yieldLimitOk <- liftIO $ decrementYieldLimit (remainingWork sv)
118 undefined yieldk single incrContinue m
122 Suspend -> liftIO $ stop sv winfo
124 enqueueFIFO sv q (RunInIO runin, m)
125 incrementYieldLimit (remainingWork sv)
129 res <- liftIO $ yield sv winfo a
130 return $ if res then Continue else Suspend
133 res <- liftIO $ yield sv winfo a
134 runInIO <- askRunInIO
135 liftIO $ enqueueFIFO sv q (runInIO, r)
136 yieldLimitOk <- liftIO $ decrementYieldLimit (remainingWork sv)
137 if res && yieldLimitOk
140 incrementYieldLimit (remainingWork sv)
143-------------------------------------------------------------------------------
145-------------------------------------------------------------------------------
147-- XXX we have this function in this file because passing runStreamLIFO as a
148-- function argument to this function results in a perf degradation of more
149-- than 10%. Need to investigate what the root cause is.
150-- Interestingly, the same thing does not make any difference for Ahead.
151getFifoSVar :: forall m a. MonadRunInIO m =>
152 RunInIO m -> Config -> IO (Channel m a)
153getFifoSVar mrun cfg = do
154 outQ <- newIORef ([], 0)
155 outQMv <- newEmptyMVar
157 wfw <- newIORef False
158 running <- newIORef Set.empty
160 yl <- case getYieldLimit cfg of
161 Nothing -> return Nothing
162 Just x -> Just <$> newIORef x
163 rateInfo <- newRateInfo cfg
165 stats <- newSVarStats
168 let isWorkFinished _ = nullQ q
169 let isWorkFinishedLimited sv = do
171 case remainingWork sv of
175 Nothing -> return False
177 return $ qEmpty || yieldsDone
179 let getSVar :: Channel m a
180 -> (Channel m a -> m [ChildEvent a])
181 -> (Channel m a -> m Bool)
182 -> (Channel m a -> IO Bool)
183 -> (LinkedQueue (RunInIO m, K.StreamK m a)
188 getSVar sv readOutput postProc workDone wloop = Channel
191 , maxBufferLimit = getMaxBuffer cfg
192 , maxWorkerLimit = min (getMaxThreads cfg) (getMaxBuffer cfg)
193 , yieldRateInfo = rateInfo
194 , outputDoorBell = outQMv
195 , readOutputQ = readOutput sv
196 , postProcess = postProc sv
197 , workerThreads = running
198 , workLoop = wloop q sv
199 , enqueue = \_ -> enqueueFIFO sv q
200 , eagerDispatch = return ()
201 , isWorkDone = workDone sv
202 , isQueueDone = workDone sv
203 , doorBellOnWorkQ = wfw
205 , workerCount = active
206 , accountThread = delThread running
207 , workerStopMVar = undefined
209 , svarInspectMode = getInspectMode cfg
215 case getStreamRate cfg of
217 case getYieldLimit cfg of
218 Nothing -> getSVar sv (readOutputQBounded False)
222 Just _ -> getSVar sv (readOutputQBounded False)
224 isWorkFinishedLimited
227 case getYieldLimit cfg of
228 Nothing -> getSVar sv readOutputQPaced
232 Just _ -> getSVar sv readOutputQPaced
234 isWorkFinishedLimited
238-- XXX GHC: If instead of MonadAsync we use (MonadIO m, MonadBaseControl IO m)
239-- constraint we get a 2x perf regression. Need to look into that.
241-- | Create a new async style concurrent stream evaluation channel. The monad
242-- state used to run the stream actions is taken from the call site of
244{-# INLINABLE newChannel #-}
245{-# SPECIALIZE newChannel :: (Config -> Config) -> IO (Channel IO a) #-}
246newChannel :: MonadAsync m =>
247 (Config -> Config) -> m (Channel m a)
248newChannel modifier = do
250 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