2-- Module : Streamly.Internal.Data.SVar.Pull
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
10module Streamly.Internal.Data.SVar.Pull
18 -- * Postprocess Hook After Reading
22 -- * Release Resources
24 , cleanupSVarFromWorker
30import Control.Concurrent (myThreadId, throwTo)
31import Control.Monad (when, void)
32import Control.Monad.IO.Class (MonadIO(liftIO))
33import Data.IORef (readIORef, writeIORef)
34import Data.IORef (IORef)
35import Streamly.Internal.Control.Concurrent (MonadAsync)
36import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS)
38import qualified Data.Set as S
40import Streamly.Internal.Data.SVar.Type
41import Streamly.Internal.Data.SVar.Dispatch
43-------------------------------------------------------------------------------
44-- Reading from the workers' output queue/buffer
45-------------------------------------------------------------------------------
47{-# INLINE readOutputQBasic #-}
48readOutputQBasic :: IORef ([ChildEvent a], Int) -> IO ([ChildEvent a], Int)
49readOutputQBasic q = atomicModifyIORefCAS q $ \x -> (([],0), x)
51{-# INLINE readOutputQRaw #-}
52readOutputQRaw :: SVar t m a -> IO ([ChildEvent a], Int)
54 (list, len) <- readOutputQBasic (outputQueue sv)
55 when (svarInspectMode sv) $ do
56 let ref = maxOutQSize $ svarStats sv
57 oqLen <- readIORef ref
58 when (len > oqLen) $ writeIORef ref len
61readOutputQBounded :: MonadAsync m => SVar t m a -> m [ChildEvent a]
62readOutputQBounded sv = do
63 (list, len) <- liftIO $ readOutputQRaw sv
64 -- When there is no output seen we dispatch more workers to help
65 -- out if there is work pending in the work queue.
69 -- send a worker proactively, if needed, even before we start
70 -- processing the output. This may degrade single processor
71 -- perf but improves multi-processor, because of more
79 cnt <- liftIO $ readIORef $ workerCount sv
81 done <- liftIO $ isWorkDone sv
82 when (not done) (pushWorker 0 sv)
84 {-# INLINE blockingRead #-}
86 sendWorkerWait sendWorkerDelay (dispatchWorker 0) sv
87 liftIO (fst `fmap` readOutputQRaw sv)
89readOutputQPaced :: MonadAsync m => SVar t m a -> m [ChildEvent a]
90readOutputQPaced sv = do
91 (list, len) <- liftIO $ readOutputQRaw sv
95 -- XXX send a worker proactively, if needed, even before we start
96 -- processing the output.
97 void $ dispatchWorkerPaced sv
102 {-# INLINE blockingRead #-}
104 sendWorkerWait sendWorkerDelayPaced dispatchWorkerPaced sv
105 liftIO (fst `fmap` readOutputQRaw sv)
107postProcessPaced :: MonadAsync m => SVar t m a -> m Bool
108postProcessPaced sv = do
109 workersDone <- allThreadsDone sv
110 -- XXX If during consumption we figure out we are getting delayed then we
111 -- should trigger dispatch there as well. We should try to check on the
112 -- workers after consuming every n item from the buffer?
115 r <- liftIO $ isWorkDone sv
117 void $ dispatchWorkerPaced sv
118 -- Note that we need to guarantee a worker since the work is not
119 -- finished, therefore we cannot just rely on dispatchWorkerPaced
120 -- which may or may not send a worker.
121 noWorker <- allThreadsDone sv
122 when noWorker $ pushWorker 0 sv
126postProcessBounded :: MonadAsync m => SVar t m a -> m Bool
127postProcessBounded sv = do
128 workersDone <- allThreadsDone sv
129 -- There may still be work pending even if there are no workers pending
130 -- because all the workers may return if the outputQueue becomes full. In
131 -- that case send off a worker to kickstart the work again.
133 -- Note that isWorkDone can only be safely checked if all workers are done.
134 -- When some workers are in progress they may have decremented the yield
135 -- Limit and later ending up incrementing it again. If we look at the yield
136 -- limit in that window we may falsely say that it is 0 and therefore we
140 r <- liftIO $ isWorkDone sv
141 -- Note that we need to guarantee a worker, therefore we cannot just
142 -- use dispatchWorker which may or may not send a worker.
143 when (not r) (pushWorker 0 sv)
144 -- XXX do we need to dispatch many here?
145 -- void $ dispatchWorker sv
149-------------------------------------------------------------------------------
151-------------------------------------------------------------------------------
153cleanupSVar :: SVar t m a -> IO ()
155 workers <- readIORef (workerThreads sv)
156 Prelude.mapM_ (`throwTo` ThreadAbort)
159cleanupSVarFromWorker :: SVar t m a -> IO ()
160cleanupSVarFromWorker sv = do
161 workers <- readIORef (workerThreads sv)
163 Prelude.mapM_ (`throwTo` ThreadAbort)
164 (Prelude.filter (/= self) $ S.toList workers)
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