1{-# OPTIONS_GHC -fno-warn-deprecations #-}
2#ifdef __HADDOCK_VERSION__
7{-# LANGUAGE TemplateHaskell #-}
8{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
12-- Module : Streamly.Internal.Data.Stream.SVar.Generate
13-- Copyright : (c) 2017 Composewell Technologies
14-- License : BSD-3-Clause
15-- Maintainer : streamly@composewell.com
16-- Stability : experimental
20module Streamly.Internal.Data.Stream.SVar.Generate
35import Control.Exception (fromException)
36import Control.Monad (when)
37import Control.Monad.Catch (throwM)
38import Control.Monad.IO.Class (MonadIO(liftIO))
39import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
40import Data.Maybe (isNothing)
41import Streamly.Internal.Control.Concurrent (MonadAsync, askRunInIO)
42import Streamly.Internal.Data.Stream.Serial (SerialT)
43import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
44import System.Mem (performMajorGC)
46import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
47 (Stream(..), Step(..))
48import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
49 (Stream, foldStreamShared, mkStream, foldStream)
50import qualified Streamly.Internal.Data.Stream.Serial as Stream (fromStreamK)
52import Streamly.Internal.Data.SVar
55import Control.Exception (Exception)
56import Control.Monad.Catch (MonadThrow)
57import Control.Monad.Trans.Control (MonadBaseControl)
58import Data.Typeable (Typeable)
59import Test.Inspection (inspect, hasNoTypeClassesExcept)
63------------------------------------------------------------------------------
64-- Generating streams from SVar
65------------------------------------------------------------------------------
69-- Usually the SVar is used to concurrently evaluate multiple actions in a
70-- stream using many worker threads that push the results to the SVar and a
71-- single puller that pulls them from SVar generating the evaluated stream.
76-- <-----------------|<--------worker
78-- output stream <------SVar<------worker
84-- The puller itself schedules the worker threads based on demand.
85-- Exceptions are propagated from the worker threads to the puller.
87-------------------------------------------------------------------------------
88-- Write a stream to an SVar
89-------------------------------------------------------------------------------
91-- XXX this errors out for Parallel/Ahead SVars
92-- | Write a stream to an 'SVar' in a non-blocking manner. The stream can then
93-- be read back from the SVar using 'fromSVar'.
94toSVar :: MonadAsync m => SVar SerialT m a -> SerialT m a -> m ()
97 liftIO $ enqueue sv (runIn, m)
98 done <- allThreadsDone sv
99 -- XXX This is safe only when called from the consumer thread or when no
100 -- consumer is present. There may be a race if we are not running in the
102 -- XXX do this only if the work queue is not empty. The work may have been
103 -- carried out by existing workers.
105 case yieldRateInfo sv of
106 Nothing -> pushWorker 0 sv
107 Just _ -> pushWorker 1 sv
109-------------------------------------------------------------------------------
110-- Read a stream from an SVar
111-------------------------------------------------------------------------------
113-- | Pull a stream from an SVar.
114{-# NOINLINE fromStreamVar #-}
115fromStreamVar :: MonadAsync m => SVar K.Stream m a -> K.Stream m a
116fromStreamVar sv = K.mkStream $ \st yld sng stp -> do
117 list <- readOutputQ sv
118 -- Reversing the output is important to guarantee that we process the
119 -- outputs in the same order as they were generated by the constituent
121 K.foldStream st yld sng stp $ processEvents $ reverse list
126 when (svarInspectMode sv) $ do
127 t <- liftIO $ getTime Monotonic
128 liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
129 liftIO $ printSVar sv "SVar Done"
132 {-# INLINE processEvents #-}
133 processEvents [] = K.mkStream $ \st yld sng stp -> do
134 done <- postProcess sv
137 else K.foldStream st yld sng stp $ fromStreamVar sv
139 processEvents (ev : es) = K.mkStream $ \st yld sng stp -> do
140 let rest = processEvents es
142 ChildYield a -> yld a rest
143 ChildStop tid e -> do
147 stop <- shouldStop tid
149 then liftIO (cleanupSVar sv) >> allDone stp
150 else K.foldStream st yld sng stp rest
152 case fromException ex of
154 K.foldStream st yld sng stp rest
155 Nothing -> liftIO (cleanupSVar sv) >> throwM ex
157 case svarStopStyle sv of
158 StopNone -> return False
159 StopAny -> return True
161 sid <- liftIO $ readIORef (svarStopBy sv)
165-- Use of GHC constraint tuple (GHC.Classes.(%,,%)) in fromStreamVar leads to
166-- space leak because the tuple gets allocated in every recursive call and each
167-- allocation holds on to the previous allocation. This test is to make sure
168-- that we do not use the constraint tuple type class.
170inspect $ hasNoTypeClassesExcept 'fromStreamVar
182-- | Generate a stream from an SVar. An unevaluated stream can be pushed to an
183-- SVar using 'toSVar'. As we pull a stream from the SVar the input stream
184-- gets evaluated concurrently. The evaluation depends on the SVar style and
185-- the configuration parameters e.g. using the maxBuffer/maxThreads
188{-# INLINE fromSVar #-}
189fromSVar :: MonadAsync m => SVar K.Stream m a -> SerialT m a
191 Stream.fromStreamK $ K.mkStream $ \st yld sng stp -> do
192 ref <- liftIO $ newIORef ()
193 _ <- liftIO $ mkWeakIORef ref hook
194 -- We pass a copy of sv to fromStreamVar, so that we know that it has
195 -- no other references, when that copy gets garbage collected "ref"
196 -- will get garbage collected and our hook will be called.
197 K.foldStreamShared st yld sng stp $
198 fromStreamVar sv{svarRef = Just ref}
202 when (svarInspectMode sv) $ do
203 r <- liftIO $ readIORef (svarStopTime (svarStats sv))
205 printSVar sv "SVar Garbage Collected"
207 -- If there are any SVars referenced by this SVar a GC will prompt
208 -- them to be cleaned up quickly.
209 when (svarInspectMode sv) performMajorGC
211data FromSVarState t m a =
213 | FromSVarRead (SVar t m a)
214 | FromSVarLoop (SVar t m a) [ChildEvent a]
215 | FromSVarDone (SVar t m a)
217-- | Like 'fromSVar' but generates a StreamD style stream instead of CPS.
219{-# INLINE_NORMAL fromSVarD #-}
220fromSVarD :: (MonadAsync m) => SVar t m a -> D.Stream m a
221fromSVarD svar = D.Stream step FromSVarInit
224 {-# INLINE_LATE step #-}
225 step _ FromSVarInit = do
226 ref <- liftIO $ newIORef ()
227 _ <- liftIO $ mkWeakIORef ref hook
228 -- when this copy of svar gets garbage collected "ref" will get
229 -- garbage collected and our GC hook will be called.
230 let sv = svar{svarRef = Just ref}
231 return $ D.Skip (FromSVarRead sv)
235 {-# NOINLINE hook #-}
237 when (svarInspectMode svar) $ do
238 r <- liftIO $ readIORef (svarStopTime (svarStats svar))
240 printSVar svar "SVar Garbage Collected"
242 -- If there are any SVars referenced by this SVar a GC will prompt
243 -- them to be cleaned up quickly.
244 when (svarInspectMode svar) performMajorGC
246 step _ (FromSVarRead sv) = do
247 list <- readOutputQ sv
248 -- Reversing the output is important to guarantee that we process the
249 -- outputs in the same order as they were generated by the constituent
251 return $ D.Skip $ FromSVarLoop sv (Prelude.reverse list)
253 step _ (FromSVarLoop sv []) = do
254 done <- postProcess sv
255 return $ D.Skip $ if done
259 step _ (FromSVarLoop sv (ev : es)) = do
261 ChildYield a -> return $ D.Yield a (FromSVarLoop sv es)
262 ChildStop tid e -> do
266 stop <- shouldStop tid
269 liftIO (cleanupSVar sv)
270 return $ D.Skip (FromSVarDone sv)
271 else return $ D.Skip (FromSVarLoop sv es)
273 case fromException ex of
275 return $ D.Skip (FromSVarLoop sv es)
276 Nothing -> liftIO (cleanupSVar sv) >> throwM ex
280 case svarStopStyle sv of
281 StopNone -> return False
282 StopAny -> return True
284 sid <- liftIO $ readIORef (svarStopBy sv)
287 step _ (FromSVarDone sv) = do
288 when (svarInspectMode sv) $ do
289 t <- liftIO $ getTime Monotonic
290 liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
291 liftIO $ printSVar sv "SVar Done"
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