@@ -183,6 +183,9 @@ module Streamly.Internal.Data.StreamK
184 , interleave
184 , interleave
186+ -- * Resource Management
186 -- * Utilities
189 -- * Utilities
@@ -193,7 +196,10 @@ where
193#include "inline.hs"
196#include "inline.hs"
194#include "assert.hs"
197#include "assert.hs"
199+import Control.Exception (mask_)
196import Control.Monad (void, join)
200import Control.Monad (void, join)
201+import Control.Monad.IO.Class (MonadIO(..))
202+import Control.Monad.Catch (MonadCatch)
197import Data.Proxy (Proxy(..))
203import Data.Proxy (Proxy(..))
198import GHC.Types (SPEC(..))
204import GHC.Types (SPEC(..))
199import Streamly.Internal.Data.Array.Type (Array(..))
205import Streamly.Internal.Data.Array.Type (Array(..))
@@ -202,7 +208,10 @@ import Streamly.Internal.Data.Producer.Type (Producer(..))
202import Streamly.Internal.Data.SVar.Type (adaptState, defState)
208import Streamly.Internal.Data.SVar.Type (adaptState, defState)
203import Streamly.Internal.Data.Unbox (sizeOf, Unbox)
209import Streamly.Internal.Data.Unbox (sizeOf, Unbox)
204import Streamly.Internal.Data.Parser.ParserK.Type (ParserK)
210import Streamly.Internal.Data.Parser.ParserK.Type (ParserK)
211+import Streamly.Internal.Data.IOFinalizer
212+ (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
214+import qualified Control.Monad.Catch as MC
206import qualified Streamly.Internal.Data.Array.Type as Array
215import qualified Streamly.Internal.Data.Array.Type as Array
207import qualified Streamly.Internal.Data.Fold.Type as FL
216import qualified Streamly.Internal.Data.Fold.Type as FL
208import qualified Streamly.Internal.Data.Parser as Parser
217import qualified Streamly.Internal.Data.Parser as Parser
@@ -917,6 +926,50 @@ mapMaybe f = go
917 Nothing -> foldStream (adaptState st) yieldk single stp r
926 Nothing -> foldStream (adaptState st) yieldk single stp r
918 in foldStream (adaptState st) yieldk single stp m1
927 in foldStream (adaptState st) yieldk single stp m1
930+-- 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.
933+-- XXX To implement a prompt cleanup, we will have to yield a cleanup function
934+-- via the yield continuation. A chain of cleanup functions can be built and
935+-- the entire chain can be invoked when the stream ends voluntarily or if
936+-- someone decides to abandon the stream.
939+bracketIO :: (MonadIO m, MonadCatch m)
940+ => IO b -> (b -> IO c) -> (b -> StreamK m a) -> StreamK m a
941+bracketIO bef aft bet =
942+ fromStream $ Stream.bracketIO bef aft (\x -> toStream $ bet x)
945+{-# INLINABLE bracketIO #-}
946+bracketIO :: (MonadIO m, MonadCatch m)
947+ => IO b -> (b -> IO c) -> (b -> StreamK m a) -> StreamK m a
948+bracketIO bef aft bet =
950+ (r, ref) <- liftIO $ mask_ $ do
952+ ref <- newIOFinalizer (aft r)
954+ return $ go ref (bet r)
958+ go ref m1 = mkStream $ \st yld sng stp ->
960+ stop = liftIO (runIOFinalizer ref) >> stp
961+ single a = liftIO (runIOFinalizer ref) >> sng a
962+ yieldk a r = yld a $ go ref r
964+ -- Do not call the finalizer twice if it has already been
965+ -- called via stop continuation and stop continuation itself
966+ -- generated an exception. runIOFinalizer takes care of that.
967+ res <- MC.try (foldStream (adaptState st) yieldk single stop m1)
970+ Left (e :: MC.SomeException) ->
971+ liftIO (runIOFinalizer ref) >> MC.throwM e
920------------------------------------------------------------------------------
973------------------------------------------------------------------------------
921-- Serial Zipping
974-- Serial Zipping
922------------------------------------------------------------------------------
975------------------------------------------------------------------------------