2-- Module : Streamly.Internal.Data.Stream.Exception
3-- Copyright : (c) 2019 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
9module Streamly.Internal.Data.Stream.Exception
26import Control.Exception (Exception)
27import Control.Monad.Catch (MonadCatch)
28import Data.Map.Strict (Map)
29import Streamly.Internal.Control.Concurrent (MonadAsync)
30import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)
32import qualified Streamly.Internal.Data.Stream.StreamD as D
36-- >>> import qualified Streamly.Internal.Data.Stream as Stream
38------------------------------------------------------------------------------
40------------------------------------------------------------------------------
42-- | Run the action @m b@ before the stream yields its first element.
44-- Same as the following but more efficient due to fusion:
46-- >>> before action xs = Stream.nilM action <> xs
47-- >>> before action xs = Stream.concatMap (const xs) (Stream.fromEffect action)
51before :: Monad m => m b -> Stream m a -> Stream m a
52before action xs = fromStreamD $ D.before action $ toStreamD xs
54-- | Like 'after', with following differences:
56-- * action @m b@ won't run if the stream is garbage collected
57-- after partial evaluation.
58-- * Monad @m@ does not require any other constraints.
59-- * has slightly better performance than 'after'.
61-- Same as the following, but with stream fusion:
63-- >>> after_ action xs = xs <> Stream.nilM action
68after_ :: Monad m => m b -> Stream m a -> Stream m a
69after_ action xs = fromStreamD $ D.after_ action $ toStreamD xs
71-- | Run the action @m b@ whenever the stream @Stream m a@ stops normally, or
72-- if it is garbage collected after a partial lazy evaluation.
74-- The semantics of the action @m b@ are similar to the semantics of cleanup
75-- action in 'bracket'.
82 => m b -> Stream m a -> Stream m a
83after action xs = fromStreamD $ D.after action $ toStreamD xs
85-- | Run the action @m b@ if the stream aborts due to an exception. The
86-- exception is not caught, simply rethrown.
88-- /Inhibits stream fusion/
91{-# INLINE onException #-}
92onException :: MonadCatch m => m b -> Stream m a -> Stream m a
93onException action xs = fromStreamD $ D.onException action $ toStreamD xs
95-- | Like 'finally' with following differences:
97-- * action @m b@ won't run if the stream is garbage collected
98-- after partial evaluation.
99-- * does not require a 'MonadAsync' constraint.
100-- * has slightly better performance than 'finally'.
102-- /Inhibits stream fusion/
106{-# INLINE finally_ #-}
107finally_ :: MonadCatch m => m b -> Stream m a -> Stream m a
108finally_ action xs = fromStreamD $ D.finally_ action $ toStreamD xs
110-- | Run the action @m b@ whenever the stream @Stream m a@ stops normally,
111-- aborts due to an exception or if it is garbage collected after a partial
114-- The semantics of running the action @m b@ are similar to the cleanup action
115-- semantics described in 'bracket'.
117-- >>> finally release = Stream.bracket (return ()) (const release)
119-- /See also 'finally_'/
121-- /Inhibits stream fusion/
124{-# INLINE finally #-}
125finally :: (MonadAsync m, MonadCatch m) => m b -> Stream m a -> Stream m a
126finally action xs = fromStreamD $ D.finally action $ toStreamD xs
128-- | Like 'bracket' but with following differences:
130-- * alloc action @m b@ runs with async exceptions enabled
131-- * cleanup action @b -> m c@ won't run if the stream is garbage collected
132-- after partial evaluation.
133-- * does not require a 'MonadAsync' constraint.
134-- * has slightly better performance than 'bracket'.
136-- /Inhibits stream fusion/
140{-# INLINE bracket_ #-}
141bracket_ :: MonadCatch m
142 => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
143bracket_ bef aft bet = fromStreamD $
144 D.bracket_ bef aft (toStreamD . bet)
146-- | Run the alloc action @m b@ with async exceptions disabled but keeping
147-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
148-- output @b@ as input to @b -> Stream m a@ to generate an output stream.
150-- @b@ is usually a resource under the state of monad @m@, e.g. a file
151-- handle, that requires a cleanup after use. The cleanup action @b -> m c@,
152-- runs whenever the stream ends normally, due to a sync or async exception or
153-- if it gets garbage collected after a partial lazy evaluation.
155-- 'bracket' only guarantees that the cleanup action runs, and it runs with
156-- async exceptions enabled. The action must ensure that it can successfully
157-- cleanup the resource in the face of sync or async exceptions.
159-- When the stream ends normally or on a sync exception, cleanup action runs
160-- immediately in the current thread context, whereas in other cases it runs in
161-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
163-- /See also: 'bracket_'/
165-- /Inhibits stream fusion/
168{-# INLINE bracket #-}
169bracket :: (MonadAsync m, MonadCatch m)
170 => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
171bracket bef aft = bracket' bef aft aft aft
173-- For a use case of this see the "streamly-process" package. It needs to kill
174-- the process in case of exception or garbage collection, but waits for the
175-- process to terminate in normal cases.
177-- | Like 'bracket' but can use separate cleanup actions depending on the mode
178-- of termination. @bracket' before onStop onGC onException action@ runs
179-- @action@ using the result of @before@. If the stream stops, @onStop@ action
180-- is executed, if the stream is abandoned @onGC@ is executed, if the stream
181-- encounters an exception @onException@ is executed.
184{-# INLINE bracket' #-}
185bracket' :: (MonadAsync m, MonadCatch m)
192bracket' bef aft gc exc bet = fromStreamD $
193 D.bracket' bef aft exc gc (toStreamD . bet)
195-- | Like 'handle' but the exception handler is also provided with the stream
196-- that generated the exception as input. The exception handler can thus
197-- re-evaluate the stream to retry the action that failed. The exception
198-- handler can again call 'ghandle' on it to retry the action multiple times.
200-- This is highly experimental. In a stream of actions we can map the stream
201-- with a retry combinator to retry each action on failure.
203-- /Inhibits stream fusion/
207{-# INLINE ghandle #-}
208ghandle :: (MonadCatch m, Exception e)
209 => (e -> Stream m a -> Stream m a) -> Stream m a -> Stream m a
212 . D.ghandle (\e xs -> toStreamD $ handler e (fromStreamD xs))
215-- | When evaluating a stream if an exception occurs, stream evaluation aborts
216-- and the specified exception handler is run with the exception as argument.
218-- /Inhibits stream fusion/
222handle :: (MonadCatch m, Exception e)
223 => (e -> Stream m a) -> Stream m a -> Stream m a
225 fromStreamD $ D.handle (toStreamD . handler) $ toStreamD xs
228-- | @retry@ takes 3 arguments
230-- 1. A map @m@ whose keys are exceptions and values are the number of times to
231-- retry the action given that the exception occurs.
233-- 2. A handler @han@ that decides how to handle an exception when the exception
236-- 3. The stream itself that we want to run this mechanism on.
238-- When evaluating a stream if an exception occurs,
240-- 1. The stream evaluation aborts
242-- 2. The exception is looked up in @m@
244-- a. If the exception exists and the mapped value is > 0 then,
246-- i. The value is decreased by 1.
248-- ii. The stream is resumed from where the exception was called, retrying
251-- b. If the exception exists and the mapped value is == 0 then the stream
254-- c. If the exception does not exist then we handle the exception using
260retry :: (MonadCatch m, Exception e, Ord e)
262 -- ^ map from exception to retry count
264 -- ^ default handler for those exceptions that are not in the map
267retry emap handler inp =
268 fromStreamD $ D.retry emap (toStreamD . handler) $ toStreamD inp
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