on master help sign in
Add bracket' combinator

It is a flavor of bracket that can take separate cleanup handlers for the cases when the stream is garbage collected, terminates on exception or terminates normally.

commit1e00573b344d73d84b7de59a3840fb8a5275b8b5 authorHarendra Kumar <harendra@composewell.com> dateWed 28 Jul 2021, 19:15 (5 years ago) parent3017898
2 files changed+45−20
src/Streamly/Internal/Data/Stream/IsStream/Exception.hs+19−2 src/Streamly/Internal/Data/Stream/StreamD/Exception.hs+26−18
src/Streamly/Internal/Data/Stream/IsStream/Exception.hs+19−2
@@ -13,6 +13,7 @@ module Streamly.Internal.Data.Stream.IsStream.Exception
1313 , after
1414 , bracket_
1515 , bracket
16+ , bracket'
1617 , onException
1718 , finally_
1819 , finally
@@ -166,8 +167,24 @@ bracket_ bef aft bet = D.fromStreamD $
166167{-# INLINE bracket #-}
167168bracket :: (IsStream t, MonadAsync m, MonadCatch m)
168169 => m b -> (b -> m c) -> (b -> t m a) -> t m a
169−bracket bef aft bet = D.fromStreamD $
170− D.bracket bef aft (toStreamD . bet)
170+bracket bef aft = bracket' bef aft aft aft
171+
172+-- For a use case of this see the "streamly-process" package. It needs to kill
173+-- the process in case of exception or garbage collection, but waits for the
174+-- process to terminate in normal cases.
175+--
176+-- | Like 'bracket' but can use separate cleanup actions depending on the mode
177+-- of termination. @bracket' before onStop onGC onException action@ runs
178+-- @action@ using the result of @before@. If the stream stops, @onStop@ action
179+-- is executed, if the stream is abandoned @onGC@ is executed, if the stream
180+-- encounters an exception @onException@ is executed.
181+--
182+-- /Pre-release/
183+{-# INLINE bracket' #-}
184+bracket' :: (IsStream t, MonadAsync m, MonadCatch m)
185+ => m b -> (b -> m c) -> (b -> m d) -> (b -> m e) -> (b -> t m a) -> t m a
186+bracket' bef aft gc exc bet = D.fromStreamD $
187+ D.bracket' bef aft exc gc (toStreamD . bet)
171188
172189-- | Like 'handle' but the exception handler is also provided with the stream
173190-- that generated the exception as input. The exception handler can thus
src/Streamly/Internal/Data/Stream/StreamD/Exception.hs+26−18
@@ -14,7 +14,7 @@ module Streamly.Internal.Data.Stream.StreamD.Exception
1414 , after_
1515 , after
1616 , bracket_
17− , bracket
17+ , bracket'
1818 , onException
1919 , finally_
2020 , finally
@@ -101,7 +101,9 @@ data GbracketIOState s1 s2 v wref
101101-- output @c@ as input to @c -> Stream m b@ to generate an output stream. When
102102-- generating the stream use the supplied @try@ operation @forall s. m s -> m
103103-- (Either e s)@ to catch synchronous exceptions. If an exception occurs run
104−-- the exception handler @c -> e -> Stream m b -> m (Stream m b)@.
104+-- the exception handler @c -> e -> Stream m b -> m (Stream m b)@. Note that
105+-- 'gbracket' does not rethrow the exception, it has to be done by the
106+-- exception handler if desired.
105107--
106108-- The cleanup action @c -> m d@, runs whenever the stream ends normally, due
107109-- to a sync or async exception or if it gets garbage collected after a partial
@@ -115,13 +117,14 @@ data GbracketIOState s1 s2 v wref
115117{-# INLINE_NORMAL gbracket #-}
116118gbracket
117119 :: (MonadIO m, MonadBaseControl IO m)
118− => m c -- ^ before
119− -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
120− -> (c -> m d) -- ^ after, on normal stop or GC
120+ => m c -- ^ before
121+ -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
122+ -> (c -> m d1) -- ^ on normal stop
123+ -> (c -> m d2) -- ^ on GC without normal stop or exception
121124 -> (c -> e -> Stream m b -> m (Stream m b)) -- ^ on exception
122− -> (c -> Stream m b) -- ^ stream generator
125+ -> (c -> Stream m b) -- ^ stream generator
123126 -> Stream m b
124−gbracket bef exc aft fexc fnormal =
127+gbracket bef exc aft gc fexc fnormal =
125128 Stream step GBracketIOInit
126129
127130 where
@@ -137,7 +140,7 @@ gbracket bef exc aft fexc fnormal =
137140 -- Tutorial: https://markkarpov.com/tutorial/exceptions.html
138141 (r, ref) <- liftBaseOp_ mask_ $ do
139142 r <- bef
140− ref <- newIOFinalizer (aft r)
143+ ref <- newIOFinalizer (gc r)
141144 return (r, ref)
142145 return $ Skip $ GBracketIONormal (fnormal r) r ref
143146
@@ -149,9 +152,8 @@ gbracket bef exc aft fexc fnormal =
149152 return $ Yield x (GBracketIONormal (Stream step1 s) v ref)
150153 Skip s ->
151154 return $ Skip (GBracketIONormal (Stream step1 s) v ref)
152− Stop -> do
153− runIOFinalizer ref
154− return Stop
155+ Stop ->
156+ clearingIOFinalizer ref (aft v) >> return Stop
155157 -- XXX Do not handle async exceptions, just rethrow them.
156158 Left e -> do
157159 -- Clearing of finalizer and running of exception handler must
@@ -261,12 +263,17 @@ bracket_ bef aft bet =
261263
262264-- | See 'Streamly.Internal.Data.Stream.IsStream.bracket'.
263265--
264−{-# INLINE_NORMAL bracket #-}
265−bracket :: (MonadAsync m, MonadCatch m)
266− => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
267−bracket bef aft bet =
268− gbracket bef (inline MC.try) aft
269− (\a (e :: SomeException) _ -> aft a >> return (nilM (MC.throwM e))) bet
266+{-# INLINE_NORMAL bracket' #-}
267+bracket' :: (MonadAsync m, MonadCatch m) =>
268+ m b
269+ -> (b -> m c)
270+ -> (b -> m d)
271+ -> (b -> m e)
272+ -> (b -> Stream m a)
273+ -> Stream m a
274+bracket' bef aft exc gc =
275+ gbracket bef (inline MC.try) aft gc
276+ (\a (e :: SomeException) _ -> exc a >> return (nilM (MC.throwM e)))
270277
271278data BracketState s v = BracketInit | BracketRun s v
272279
@@ -306,7 +313,8 @@ finally_ action xs = bracket_ (return ()) (\_ -> action) (const xs)
306313--
307314{-# INLINE finally #-}
308315finally :: (MonadAsync m, MonadCatch m) => m b -> Stream m a -> Stream m a
309−finally action xs = bracket (return ()) (\_ -> action) (const xs)
316+finally action xs = bracket' (return ()) act act act (const xs)
317+ where act _ = action
310318
311319-- | See 'Streamly.Internal.Data.Stream.IsStream.ghandle'.
312320--