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 as D
47 (Stream(..), Step(..))
48import qualified Streamly.Internal.Data.StreamK 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#if __GLASGOW_HASKELL__ != 906
166-- Use of GHC constraint tuple (GHC.Classes.(%,,%)) in fromStreamVar leads to
167-- space leak because the tuple gets allocated in every recursive call and each
168-- allocation holds on to the previous allocation. This test is to make sure
169-- that we do not use the constraint tuple type class.
171inspect $ hasNoTypeClassesExcept 'fromStreamVar
184-- | Generate a stream from an SVar. An unevaluated stream can be pushed to an
185-- SVar using 'toSVar'. As we pull a stream from the SVar the input stream
186-- gets evaluated concurrently. The evaluation depends on the SVar style and
187-- the configuration parameters e.g. using the maxBuffer/maxThreads
190{-# INLINE fromSVar #-}
191fromSVar :: MonadAsync m => SVar K.Stream m a -> SerialT m a
193 Stream.fromStreamK $ K.mkStream $ \st yld sng stp -> do
194 ref <- liftIO $ newIORef ()
195 _ <- liftIO $ mkWeakIORef ref hook
196 -- We pass a copy of sv to fromStreamVar, so that we know that it has
197 -- no other references, when that copy gets garbage collected "ref"
198 -- will get garbage collected and our hook will be called.
199 K.foldStreamShared st yld sng stp $
200 fromStreamVar sv{svarRef = Just ref}
204 when (svarInspectMode sv) $ do
205 r <- liftIO $ readIORef (svarStopTime (svarStats sv))
207 printSVar sv "SVar Garbage Collected"
209 -- If there are any SVars referenced by this SVar a GC will prompt
210 -- them to be cleaned up quickly.
211 when (svarInspectMode sv) performMajorGC
213data FromSVarState t m a =
215 | FromSVarRead (SVar t m a)
216 | FromSVarLoop (SVar t m a) [ChildEvent a]
217 | FromSVarDone (SVar t m a)
219-- | Like 'fromSVar' but generates a StreamD style stream instead of CPS.
221{-# INLINE_NORMAL fromSVarD #-}
222fromSVarD :: (MonadAsync m) => SVar t m a -> D.Stream m a
223fromSVarD svar = D.Stream step FromSVarInit
226 {-# INLINE_LATE step #-}
227 step _ FromSVarInit = do
228 ref <- liftIO $ newIORef ()
229 _ <- liftIO $ mkWeakIORef ref hook
230 -- when this copy of svar gets garbage collected "ref" will get
231 -- garbage collected and our GC hook will be called.
232 let sv = svar{svarRef = Just ref}
233 return $ D.Skip (FromSVarRead sv)
237 {-# NOINLINE hook #-}
239 when (svarInspectMode svar) $ do
240 r <- liftIO $ readIORef (svarStopTime (svarStats svar))
242 printSVar svar "SVar Garbage Collected"
244 -- If there are any SVars referenced by this SVar a GC will prompt
245 -- them to be cleaned up quickly.
246 when (svarInspectMode svar) performMajorGC
248 step _ (FromSVarRead sv) = do
249 list <- readOutputQ sv
250 -- Reversing the output is important to guarantee that we process the
251 -- outputs in the same order as they were generated by the constituent
253 return $ D.Skip $ FromSVarLoop sv (Prelude.reverse list)
255 step _ (FromSVarLoop sv []) = do
256 done <- postProcess sv
257 return $ D.Skip $ if done
261 step _ (FromSVarLoop sv (ev : es)) = do
263 ChildYield a -> return $ D.Yield a (FromSVarLoop sv es)
264 ChildStop tid e -> do
268 stop <- shouldStop tid
271 liftIO (cleanupSVar sv)
272 return $ D.Skip (FromSVarDone sv)
273 else return $ D.Skip (FromSVarLoop sv es)
275 case fromException ex of
277 return $ D.Skip (FromSVarLoop sv es)
278 Nothing -> liftIO (cleanupSVar sv) >> throwM ex
282 case svarStopStyle sv of
283 StopNone -> return False
284 StopAny -> return True
286 sid <- liftIO $ readIORef (svarStopBy sv)
289 step _ (FromSVarDone sv) = do
290 when (svarInspectMode sv) $ do
291 t <- liftIO $ getTime Monotonic
292 liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
293 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