2{-# LANGUAGE FlexibleContexts #-}
4#ifdef __HADDOCK_VERSION__
9{-# LANGUAGE TemplateHaskell #-}
10{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
14-- Module : Streamly.Internal.Data.Stream.SVar
15-- Copyright : (c) 2017 Harendra Kumar
18-- Maintainer : streamly@composewell.com
19-- Stability : experimental
23module Streamly.Internal.Data.Stream.SVar
33import Control.Exception (fromException)
34import Control.Monad (when, void)
35import Control.Monad.Catch (throwM)
36import Control.Monad.IO.Class (MonadIO(liftIO))
37import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
38import Data.Maybe (isNothing)
39import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
40import System.Mem (performMajorGC)
42import Streamly.Internal.Data.SVar
43import Streamly.Internal.Data.Stream.StreamK hiding (reverse)
46import Control.Exception (Exception)
47import Control.Monad.Catch (MonadThrow)
48import Control.Monad.Trans.Control (MonadBaseControl)
49import Data.Typeable (Typeable)
50import Test.Inspection (inspect, hasNoTypeClassesExcept)
53-- | Pull a stream from an SVar.
54{-# NOINLINE fromStreamVar #-}
55fromStreamVar :: MonadAsync m => SVar Stream m a -> Stream m a
56fromStreamVar sv = MkStream $ \st yld sng stp -> do
57 list <- readOutputQ sv
58 -- Reversing the output is important to guarantee that we process the
59 -- outputs in the same order as they were generated by the constituent
61 foldStream st yld sng stp $ processEvents $ reverse list
66 when (svarInspectMode sv) $ do
67 t <- liftIO $ getTime Monotonic
68 liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
69 liftIO $ printSVar sv "SVar Done"
72 {-# INLINE processEvents #-}
73 processEvents [] = MkStream $ \st yld sng stp -> do
74 done <- postProcess sv
77 else foldStream st yld sng stp $ fromStreamVar sv
79 processEvents (ev : es) = MkStream $ \st yld sng stp -> do
80 let rest = processEvents es
82 ChildYield a -> yld a rest
87 stop <- shouldStop tid
89 then liftIO (cleanupSVar sv) >> allDone stp
90 else foldStream st yld sng stp rest
92 case fromException ex of
94 foldStream st yld sng stp rest
95 Nothing -> liftIO (cleanupSVar sv) >> throwM ex
97 case svarStopStyle sv of
98 StopNone -> return False
99 StopAny -> return True
101 sid <- liftIO $ readIORef (svarStopBy sv)
102 return $ if tid == sid then True else False
105-- Use of GHC constraint tuple (GHC.Classes.(%,,%)) in fromStreamVar leads to
106-- space leak because the tuple gets allocated in every recursive call and each
107-- allocation holds on to the previous allocation. This test is to make sure
108-- that we do not use the constraint tuple type class.
110inspect $ hasNoTypeClassesExcept 'fromStreamVar
122{-# INLINE fromSVar #-}
123fromSVar :: (MonadAsync m, IsStream t) => SVar Stream m a -> t m a
125 mkStream $ \st yld sng stp -> do
126 ref <- liftIO $ newIORef ()
127 _ <- liftIO $ mkWeakIORef ref hook
128 -- We pass a copy of sv to fromStreamVar, so that we know that it has
129 -- no other references, when that copy gets garbage collected "ref"
130 -- will get garbage collected and our hook will be called.
131 foldStreamShared st yld sng stp $
132 fromStream $ fromStreamVar sv{svarRef = Just ref}
136 when (svarInspectMode sv) $ do
137 r <- liftIO $ readIORef (svarStopTime (svarStats sv))
139 printSVar sv "SVar Garbage Collected"
141 -- If there are any SVars referenced by this SVar a GC will prompt
142 -- them to be cleaned up quickly.
143 when (svarInspectMode sv) performMajorGC
145-- | Write a stream to an 'SVar' in a non-blocking manner. The stream can then
146-- be read back from the SVar using 'fromSVar'.
147toSVar :: (IsStream t, MonadAsync m) => SVar Stream m a -> t m a -> m ()
148toSVar sv m = toStreamVar sv (toStream m)
150-------------------------------------------------------------------------------
151-- Process events received by a fold consumer from a stream producer
152-------------------------------------------------------------------------------
154-- | Pull a stream from an SVar.
155{-# NOINLINE fromProducer #-}
156fromProducer :: MonadAsync m => SVar Stream m a -> Stream m a
157fromProducer sv = mkStream $ \st yld sng stp -> do
158 list <- readOutputQ sv
159 -- Reversing the output is important to guarantee that we process the
160 -- outputs in the same order as they were generated by the constituent
162 foldStream st yld sng stp $ processEvents $ reverse list
167 when (svarInspectMode sv) $ do
168 t <- liftIO $ getTime Monotonic
169 liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
170 liftIO $ printSVar sv "SVar Done"
171 sendStopToProducer sv
174 {-# INLINE processEvents #-}
175 processEvents [] = mkStream $ \st yld sng stp -> do
176 foldStream st yld sng stp $ fromProducer sv
178 processEvents (ev : es) = mkStream $ \_ yld _ stp -> do
179 let rest = processEvents es
181 ChildYield a -> yld a rest
182 ChildStop tid e -> do
185 Nothing -> allDone stp
186 Just _ -> error "Bug: fromProducer: received exception"
188-------------------------------------------------------------------------------
189-- Process events received by the producer thread from the consumer side
190-------------------------------------------------------------------------------
192-- XXX currently only one event is sent by a fold consumer to the stream
193-- producer. But we can potentially have multiple events e.g. the fold step can
194-- generate exception more than once and the producer can ignore those
195-- exceptions or handle them and still keep driving the fold.
197{-# NOINLINE fromConsumer #-}
198fromConsumer :: MonadAsync m => SVar Stream m a -> m Bool
200 (list, _) <- liftIO $ readOutputQBasic (outputQueueFromConsumer sv)
201 -- Reversing the output is important to guarantee that we process the
202 -- outputs in the same order as they were generated by the constituent
204 processEvents $ reverse list
208 {-# INLINE processEvents #-}
209 processEvents [] = return False
210 processEvents (ev : _) = do
214 Nothing -> return True
216 ChildYield _ -> error "Bug: fromConsumer: invalid ChildYield event"
218-- push values to a fold worker via an SVar. Returns whether the fold is done.
219{-# INLINE pushToFold #-}
220pushToFold :: MonadAsync m => SVar Stream m a -> a -> m Bool
222 -- Check for exceptions before decrement so that we do not
223 -- block forever if the child already exited with an exception.
225 -- We avoid a race between the consumer fold sending an event and we
226 -- blocking on decrementBufferLimit by waking up the producer thread in
227 -- sendToProducer before any event is sent by the fold to the producer
229 let qref = outputQueueFromConsumer sv
231 (_, n) <- liftIO $ readIORef qref
238 decrementBufferLimit sv
239 void $ send sv (ChildYield a)
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