{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ExistentialQuantification #-}
#ifdef USE_REFLEX_OPTIMIZER
{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
#endif
module Data.WeakBag
( WeakBag
, WeakBagTicket
, empty
, singleton
, insert
, traverse
, remove
, _weakBag_children
) where
import Prelude hiding (traverse)
import Control.Exception
import Control.Monad
import Control.Monad.IO.Class
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IntMap
import Data.IORef
import System.Mem.Weak
data WeakBag a = WeakBag
{ _weakBag_nextId :: {-# UNPACK #-} !(IORef Int)
, _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
}
data WeakBagTicket = forall a. WeakBagTicket
{ _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
, _weakBagTicket_item :: {-# NOUNPACK #-} !a
}
{-# INLINE insert #-}
insert :: a
-> WeakBag a
-> IORef (Weak b)
-> (b -> IO ())
-> IO WeakBagTicket
insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
a' <- evaluate a
wbRef' <- evaluate wbRef
myId <- atomicModifyIORef' nextId $ \n -> (succ n, n)
let cleanup = do
wb <- readIORef wbRef'
mb <- deRefWeak wb
forM_ mb $ \b -> do
csWithoutMe <- atomicModifyIORef children $ \cs ->
let !csWithoutMe = IntMap.delete myId cs
in (csWithoutMe, csWithoutMe)
when (IntMap.null csWithoutMe) $ finalizer b
wa <- mkWeakPtr a' $ Just cleanup
atomicModifyIORef' children $ \cs -> (IntMap.insert myId wa cs, ())
return $ WeakBagTicket
{ _weakBagTicket_weakItem = wa
, _weakBagTicket_item = a'
}
{-# INLINE empty #-}
empty :: IO (WeakBag a)
empty = {-# SCC "empty" #-} do
nextId <- newIORef 1
children <- newIORef IntMap.empty
let bag = WeakBag
{ _weakBag_nextId = nextId
, _weakBag_children = children
}
return bag
{-# INLINE singleton #-}
singleton :: a -> IORef (Weak b) -> (b -> IO ()) -> IO (WeakBag a, WeakBagTicket)
singleton a wbRef finalizer = {-# SCC "singleton" #-} do
bag <- empty
ticket <- insert a bag wbRef finalizer
return (bag, ticket)
{-# INLINE traverse #-}
traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
cs <- liftIO $ readIORef children
forM_ cs $ \c -> do
ma <- liftIO $ deRefWeak c
mapM_ f ma
{-# INLINE remove #-}
remove :: WeakBagTicket -> IO ()
remove (WeakBagTicket w _) = {-# SCC "remove" #-} finalize w