module General.Cleanup(
Cleanup, withCleanup, addCleanup, addCleanup_
) where
import Control.Exception
import qualified Data.HashMap.Strict as Map
import Control.Monad
import Data.IORef.Extra
import Data.List.Extra
data S = S {unique :: {-# UNPACK #-} !Int, items :: !(Map.HashMap Int (IO ()))}
newtype Cleanup = Cleanup (IORef S)
withCleanup :: (Cleanup -> IO a) -> IO a
withCleanup act = do
ref <- newIORef $ S 0 Map.empty
act (Cleanup ref) `finally` runCleanup (Cleanup ref)
runCleanup :: Cleanup -> IO ()
runCleanup (Cleanup ref) = do
items <- atomicModifyIORef' ref $ \s -> (s{items=Map.empty}, items s)
mapM_ snd $ sortOn (negate . fst) $ Map.toList items
addCleanup :: Cleanup -> IO () -> IO (IO ())
addCleanup (Cleanup ref) act = atomicModifyIORef' ref $ \s -> let i = unique s in
(,) (S (unique s + 1) (Map.insert i act $ items s)) $
atomicModifyIORef' ref $ \s -> (s{items = Map.delete i $ items s}, ())
addCleanup_ :: Cleanup -> IO () -> IO ()
addCleanup_ c act = void $ addCleanup c act