on master help sign in
Throw the exception promptly in bracketIO
commit87cb22cc9968569d20480546123ae3e36fad3d30 authorHarendra Kumar <harendra@composewell.com> dateTue 1 Aug 2023, 20:22 (3 years ago) parent1b21c18
5 files changed+59−13
core/docs/Changelog.md+1 core/src/Streamly/Data/Stream.hs+22−2 core/src/Streamly/Internal/Data/Stream/StreamD/Exception.hs+23−4 docs/User/Project/Changelog.md+2 src/Streamly/Internal/Data/Stream/Exception/Lifted.hs+11−7
core/docs/Changelog.md+1
@@ -10,6 +10,7 @@
1010
1111* Changed the signature of 'Streamly.Data.Stream.handle' to make the exception
1212 handler monadic.
13+* Rethrow the exception promptly in bracketIO.
1314
1415### Deprecations
1516
core/src/Streamly/Data/Stream.hs+22−2
@@ -466,7 +466,16 @@ module Streamly.Data.Stream
466466 -- in Exception.Base
467467
468468 -- * Exceptions
469− -- | Most of these combinators inhibit stream fusion, therefore, when
469+ -- | Note that the stream exception handling routines catch and handle
470+ -- exceptions only in the stream generation steps and not in the consumer
471+ -- of the stream. For example, if we are folding or parsing a stream - any
472+ -- exceptions in the fold or parse steps won't be observed by the stream
473+ -- exception handlers. Exceptions in the fold or parse steps can be handled
474+ -- using the fold or parse exception handling routines. You can wrap the
475+ -- stream elimination function in the monad exception handler to observe
476+ -- exceptions in the stream as well as the consumer.
477+ --
478+ -- Most of these combinators inhibit stream fusion, therefore, when
470479 -- possible, they should be called in an outer loop to mitigate the cost.
471480 -- For example, instead of calling them on a stream of chars call them on a
472481 -- stream of arrays before flattening it to a stream of chars.
@@ -481,8 +490,19 @@ module Streamly.Data.Stream
481490 -- | 'bracket' is the most general resource management operation, all other
482491 -- operations can be expressed using it. These functions have IO suffix
483492 -- because the allocation and cleanup functions are IO actions. For
484− -- generalized allocation and cleanup functions see the functions without
493+ -- generalized allocation and cleanup functions, see the functions without
485494 -- the IO suffix in the "streamly" package.
495+ --
496+ -- Note that these operations bracket the stream generation only, they do
497+ -- not cover the stream consumer. This means if an exception occurs in
498+ -- the consumer of the stream (e.g. in a fold or parse step) then the
499+ -- exception won't be observed by the stream resource handlers, in that
500+ -- case the resource cleanup handler runs when the stream is garbage
501+ -- collected.
502+ --
503+ -- Monad level resource management can always be used around the stream
504+ -- elimination functions, such a function can observe exceptions in both
505+ -- the stream and its consumer.
486506 , before
487507 , afterIO
488508 , finallyIO
core/src/Streamly/Internal/Data/Stream/StreamD/Exception.hs+23−4
@@ -257,6 +257,9 @@ afterIO action (Stream step state) = Stream step' Nothing
257257-- | Run the action @m b@ if the stream evaluation is aborted due to an
258258-- exception. The exception is not caught, simply rethrown.
259259--
260+-- Observes exceptions only in the stream generation, and not in stream
261+-- consumers.
262+--
260263-- /Inhibits stream fusion/
261264--
262265{-# INLINE_NORMAL onException #-}
@@ -320,6 +323,8 @@ bracketUnsafe bef aft =
320323-- stream is abandoned @onGC@ is executed, if the stream encounters an
321324-- exception @onException@ is executed.
322325--
326+-- The exception is not caught, it is rethrown.
327+--
323328-- /Inhibits stream fusion/
324329--
325330-- /Pre-release/
@@ -335,18 +340,20 @@ bracketIO3 bef aft onExc onGC =
335340 gbracket
336341 bef
337342 aft
338− (\a (e :: SomeException) _ -> onExc a >> return (nilM (MC.throwM e)))
343+ (\a (e :: SomeException) _ -> onExc a >> MC.throwM e)
339344 onGC
340345 (inline MC.try)
341346
342347-- | Run the alloc action @IO b@ with async exceptions disabled but keeping
343348-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
344−-- output @b@ as input to @b -> Stream m a@ to generate an output stream.
349+-- output @b@ of the IO action as input to the function @b -> Stream m a@ to
350+-- generate an output stream.
345351--
346352-- @b@ is usually a resource under the IO monad, e.g. a file handle, that
347353-- requires a cleanup after use. The cleanup action @b -> IO c@, runs whenever
348−-- the stream ends normally, due to a sync or async exception or if it gets
349−-- garbage collected after a partial lazy evaluation.
354+-- (1) the stream ends normally, (2) due to a sync or async exception or, (3)
355+-- if it gets garbage collected after a partial lazy evaluation. The exception
356+-- is not caught, it is rethrown.
350357--
351358-- 'bracketIO' only guarantees that the cleanup action runs, and it runs with
352359-- async exceptions enabled. The action must ensure that it can successfully
@@ -355,6 +362,12 @@ bracketIO3 bef aft onExc onGC =
355362-- When the stream ends normally or on a sync exception, cleanup action runs
356363-- immediately in the current thread context, whereas in other cases it runs in
357364-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
365+-- An example where GC based cleanup happens is when a stream is being folded
366+-- but the fold terminates without draining the entire stream or if the
367+-- consumer of the stream encounters an exception.
368+--
369+-- Observes exceptions only in the stream generation, and not in stream
370+-- consumers.
358371--
359372-- /See also: 'bracketUnsafe'/
360373--
@@ -443,6 +456,12 @@ ghandle f stream =
443456
444457-- | When evaluating a stream if an exception occurs, stream evaluation aborts
445458-- and the specified exception handler is run with the exception as argument.
459+-- The exception is caught and handled unless the handler decides to rethrow
460+-- it. Note that exception handling is not applied to the stream returned by
461+-- the exception handler.
462+--
463+-- Observes exceptions only in the stream generation, and not in stream
464+-- consumers.
446465--
447466-- /Inhibits stream fusion/
448467--
docs/User/Project/Changelog.md+2
@@ -11,6 +11,8 @@
1111 breaking change in `transformers` 0.6. You can replace `lift` with
1212 `fromEffect` when using these as top level monads in a monad stack.
1313
14+* Rethrow the exception promptly in bracketIO.
15+
1416## 0.9.0 (Mar 2023)
1517
1618Also see the following:
src/Streamly/Internal/Data/Stream/Exception/Lifted.hs+11−7
@@ -124,7 +124,7 @@ bracket3D bef aft onExc onGC =
124124 gbracket
125125 bef
126126 aft
127− (\a (e :: SomeException) _ -> onExc a >> return (D.nilM (MC.throwM e)))
127+ (\a (e :: SomeException) _ -> onExc a >> MC.throwM e)
128128 onGC
129129 (inline MC.try)
130130
@@ -144,6 +144,8 @@ bracket3D bef aft onExc onGC =
144144-- stream is abandoned @onGC@ is executed, if the stream encounters an
145145-- exception @onException@ is executed.
146146--
147+-- The exception is not caught, it is rethrown.
148+--
147149-- /Pre-release/
148150{-# INLINE bracket3 #-}
149151bracket3 :: (MonadAsync m, MonadCatch m)
@@ -155,14 +157,16 @@ bracket3 :: (MonadAsync m, MonadCatch m)
155157 -> Stream m a
156158bracket3 = bracket3D
157159
158−-- | Run the alloc action @m b@ with async exceptions disabled but keeping
160+-- | Run the alloc action @IO b@ with async exceptions disabled but keeping
159161-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
160−-- output @b@ as input to @b -> Stream m a@ to generate an output stream.
162+-- output @b@ of the IO action as input to the function @b -> Stream m a@ to
163+-- generate an output stream.
161164--
162−-- @b@ is usually a resource under the state of monad @m@, e.g. a file
163−-- handle, that requires a cleanup after use. The cleanup action @b -> m c@,
164−-- runs whenever the stream ends normally, due to a sync or async exception or
165−-- if it gets garbage collected after a partial lazy evaluation.
165+-- @b@ is usually a resource under the IO monad, e.g. a file handle, that
166+-- requires a cleanup after use. The cleanup action @b -> m c@, runs whenever
167+-- (1) the stream ends normally, (2) due to a sync or async exception or, (3)
168+-- if it gets garbage collected after a partial lazy evaluation. The exception
169+-- is not caught, it is rethrown.
166170--
167171-- 'bracket' only guarantees that the cleanup action runs, and it runs with
168172-- async exceptions enabled. The action must ensure that it can successfully