2-- Module : Streamly.Internal.Data.Unfold.Exception
3-- Copyright : (c) 2019 Composewell Technologies
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
9-- Lifted resource management primitives.
11module Streamly.Internal.Data.Unfold.Exception
22import Control.Exception (mask_)
23import Control.Monad.Catch (MonadCatch)
24import Streamly.Internal.Control.Concurrent
25 (MonadRunInIO, MonadAsync, withRunInIO)
26import Streamly.Internal.Data.IOFinalizer.Lifted
27 (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
28import qualified Control.Monad.Catch as MC
30import Streamly.Internal.Data.Unfold
32-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
33-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
34-- output @c@ as input to @Unfold m c b@ to generate an output stream. When
35-- unfolding use the supplied @try@ operation @forall s. m s -> m (Either e s)@
36-- to catch synchronous exceptions. If an exception occurs run the exception
37-- handling unfold @Unfold m (c, e) b@.
39-- The cleanup action @c -> m d@, runs whenever the stream ends normally, due
40-- to a sync or async exception or if it gets garbage collected after a partial
41-- lazy evaluation. See 'bracket' for the semantics of the cleanup action.
43-- 'gbracket' can express all other exception handling combinators.
45-- /Inhibits stream fusion/
48{-# INLINE_NORMAL gbracket #-}
51 => (a -> m c) -- ^ before
52 -> (c -> m d) -- ^ after, on normal stop, or GC
53 -> Unfold m (c, e) b -- ^ on exception
54 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
55 -> Unfold m c b -- ^ unfold to run
57gbracket bef aft (Unfold estep einject) ftry (Unfold step1 inject1) =
63 -- Mask asynchronous exceptions to make the execution of 'bef' and
64 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
65 (r, ref) <- withRunInIO $ \run -> mask_ $ run $ do
67 ref <- newIOFinalizer (aft r)
70 return $ Right (s, r, ref)
72 {-# INLINE_LATE step #-}
73 step (Right (st, v, ref)) = do
74 res <- ftry $ step1 st
77 Yield x s -> return $ Yield x (Right (s, v, ref))
78 Skip s -> return $ Skip (Right (s, v, ref))
82 -- XXX Do not handle async exceptions, just rethrow them.
84 -- Clearing of finalizer and running of exception handler must
85 -- be atomic wrt async exceptions. Otherwise if we have cleared
86 -- the finalizer and have not run the exception handler then we
87 -- may leak the resource.
88 r <- clearingIOFinalizer ref (einject (v, e))
89 return $ Skip (Left r)
93 Yield x s -> Yield x (Left s)
94 Skip s -> Skip (Left s)
97-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
98-- the unfold stops normally, or if it is garbage collected after a partial
101-- The semantics of the action @a -> m c@ are similar to the cleanup action
102-- semantics in 'bracket'.
104-- /See also 'after_'/
107{-# INLINE_NORMAL after #-}
108after :: MonadRunInIO m
109 => (a -> m c) -> Unfold m a b -> Unfold m a b
110after action (Unfold step1 inject1) = Unfold step inject
116 ref <- newIOFinalizer (action x)
119 {-# INLINE_LATE step #-}
123 Yield x s -> return $ Yield x (s, ref)
124 Skip s -> return $ Skip (s, ref)
129-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
130-- the unfold stops normally, aborts due to an exception or if it is garbage
131-- collected after a partial lazy evaluation.
133-- The semantics of the action @a -> m c@ are similar to the cleanup action
134-- semantics in 'bracket'.
137-- finally release = bracket return release
140-- /See also 'finally_'/
142-- /Inhibits stream fusion/
145{-# INLINE_NORMAL finally #-}
146finally :: (MonadAsync m, MonadCatch m)
147 => (a -> m c) -> Unfold m a b -> Unfold m a b
148finally action (Unfold step1 inject1) = Unfold step inject
154 ref <- newIOFinalizer (action x)
157 {-# INLINE_LATE step #-}
159 res <- step1 st `MC.onException` runIOFinalizer ref
161 Yield x s -> return $ Yield x (s, ref)
162 Skip s -> return $ Skip (s, ref)
167-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
168-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
169-- output @c@ as input to @Unfold m c b@ to generate an output stream.
171-- @c@ is usually a resource under the state of monad @m@, e.g. a file
172-- handle, that requires a cleanup after use. The cleanup action @c -> m d@,
173-- runs whenever the stream ends normally, due to a sync or async exception or
174-- if it gets garbage collected after a partial lazy evaluation.
176-- 'bracket' only guarantees that the cleanup action runs, and it runs with
177-- async exceptions enabled. The action must ensure that it can successfully
178-- cleanup the resource in the face of sync or async exceptions.
180-- When the stream ends normally or on a sync exception, cleanup action runs
181-- immediately in the current thread context, whereas in other cases it runs in
182-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
184-- /See also: 'bracket_', 'gbracket'/
186-- /Inhibits stream fusion/
189{-# INLINE_NORMAL bracket #-}
190bracket :: (MonadAsync m, MonadCatch m)
191 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
192bracket bef aft (Unfold step1 inject1) = Unfold step inject
197 -- Mask asynchronous exceptions to make the execution of 'bef' and
198 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
199 (r, ref) <- withRunInIO $ \run -> mask_ $ run $ do
201 ref <- newIOFinalizer (aft r)
206 {-# INLINE_LATE step #-}
208 res <- step1 st `MC.onException` runIOFinalizer ref
210 Yield x s -> return $ Yield x (s, ref)
211 Skip s -> return $ Skip (s, ref)
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