@@ -183,6 +183,9 @@ module Streamly.Internal.Data.StreamK
184 , interleave
184 , interleave
186 -- * Resource Management
189 -- * Resource Management
187 , bracketIO
190 , bracketIO
@@ -196,20 +199,19 @@ where
196#include "inline.hs"
199#include "inline.hs"
197#include "assert.hs"
200#include "assert.hs"
199−import Control.Exception (mask_)
202+import Control.Exception (mask_, Exception)
200import Control.Monad (void, join)
203import Control.Monad (void, join)
201−import Control.Monad.IO.Class (MonadIO(..))
202import Control.Monad.Catch (MonadCatch)
204import Control.Monad.Catch (MonadCatch)
205+import Control.Monad.IO.Class (MonadIO(..))
203import Data.Proxy (Proxy(..))
206import Data.Proxy (Proxy(..))
204import GHC.Types (SPEC(..))
207import GHC.Types (SPEC(..))
205import Streamly.Internal.Data.Array.Type (Array(..))
208import Streamly.Internal.Data.Array.Type (Array(..))
206import Streamly.Internal.Data.Fold.Type (Fold(..))
209import Streamly.Internal.Data.Fold.Type (Fold(..))
210+import Streamly.Internal.Data.IOFinalizer (newIOFinalizer, runIOFinalizer)
211+import Streamly.Internal.Data.Parser.ParserK.Type (ParserK)
207import Streamly.Internal.Data.Producer.Type (Producer(..))
212import Streamly.Internal.Data.Producer.Type (Producer(..))
208import Streamly.Internal.Data.SVar.Type (adaptState, defState)
213import Streamly.Internal.Data.SVar.Type (adaptState, defState)
209import Streamly.Internal.Data.Unbox (sizeOf, Unbox)
214import Streamly.Internal.Data.Unbox (sizeOf, Unbox)
210−import Streamly.Internal.Data.Parser.ParserK.Type (ParserK)
211−import Streamly.Internal.Data.IOFinalizer
212− (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
214import qualified Control.Monad.Catch as MC
216import qualified Control.Monad.Catch as MC
215import qualified Streamly.Internal.Data.Array.Type as Array
217import qualified Streamly.Internal.Data.Array.Type as Array
@@ -926,6 +928,38 @@ mapMaybe f = go
926 Nothing -> foldStream (adaptState st) yieldk single stp r
928 Nothing -> foldStream (adaptState st) yieldk single stp r
927 in foldStream (adaptState st) yieldk single stp m1
929 in foldStream (adaptState st) yieldk single stp m1
931+-------------------------------------------------------------------------------
932+-- Exception Handling
933+-------------------------------------------------------------------------------
935+-- | Like 'Streamly.Data.Stream.handle' but with one significant difference,
936+-- this function observes exceptions from the consumer of the stream as well.
938+-- You can also convert 'StreamK' to 'Stream' and use exception handling from
941+-- >>> handle f s = StreamK.fromStream $ Stream.handle (\e -> StreamK.toStream (f e)) (StreamK.toStream s)
943+{-# INLINABLE handle #-}
944+handle :: (MonadCatch m, Exception e)
945+ => (e -> m (StreamK m a)) -> StreamK m a -> StreamK m a
946+handle f stream = go stream
950+ go m1 = mkStream $ \st yld sng stp ->
951+ let yieldk a r = yld a $ go r
953+ res <- MC.try (foldStream (adaptState st) yieldk sng stp m1)
958+ foldStream (adaptState st) yld sng stp r
960+-------------------------------------------------------------------------------
961+-- Resource Management
962+-------------------------------------------------------------------------------
930-- If we are folding the stream and we do not drain the entire stream (e.g. if
964-- If we are folding the stream and we do not drain the entire stream (e.g. if
931-- the fold terminates before the stream) then the finalizer will run on GC.
965-- the fold terminates before the stream) then the finalizer will run on GC.
@@ -935,13 +969,16 @@ mapMaybe f = go
935-- the entire chain can be invoked when the stream ends voluntarily or if
969-- the entire chain can be invoked when the stream ends voluntarily or if
936-- someone decides to abandon the stream.
970-- someone decides to abandon the stream.
938−{-
972+-- | Like 'Streamly.Data.Stream.bracketIO' but with one significant difference,
939−bracketIO :: (MonadIO m, MonadCatch m)
973+-- this function observes exceptions from the consumer of the stream as well.
940− => IO b -> (b -> IO c) -> (b -> StreamK m a) -> StreamK m a
974+-- Therefore, it cleans up the resource promptly when the consumer encounters
941−bracketIO bef aft bet =
975+-- an exception.
977+-- You can also convert 'StreamK' to 'Stream' and use resource handling from
942− fromStream $ Stream.bracketIO bef aft (\x -> toStream $ bet x)
980+-- >>> bracketIO bef aft bet = StreamK.fromStream $ Stream.bracketIO bef aft (StreamK.toStream . bet)
945{-# INLINABLE bracketIO #-}
982{-# INLINABLE bracketIO #-}
946bracketIO :: (MonadIO m, MonadCatch m)
983bracketIO :: (MonadIO m, MonadCatch m)
947 => IO b -> (b -> IO c) -> (b -> StreamK m a) -> StreamK m a
984 => IO b -> (b -> IO c) -> (b -> StreamK m a) -> StreamK m a
@@ -957,6 +994,8 @@ bracketIO bef aft bet =
958 go ref m1 = mkStream $ \st yld sng stp ->
995 go ref m1 = mkStream $ \st yld sng stp ->
997+ -- We can discard exceptions on continuations to make it equivalent
998+ -- to StreamD, but it seems like a desirable behavior.
960 stop = liftIO (runIOFinalizer ref) >> stp
999 stop = liftIO (runIOFinalizer ref) >> stp
961 single a = liftIO (runIOFinalizer ref) >> sng a
1000 single a = liftIO (runIOFinalizer ref) >> sng a
962 yieldk a r = yld a $ go ref r
1001 yieldk a r = yld a $ go ref r