lifetimes-0.1.0.0: src/Lifetimes/Rc.hs
{-# LANGUAGE NamedFieldPuns #-}
-- | Module: Lifetimes.Rc
-- Description: Support for working with reference-counted resources.
--
-- Rather than associating a resource with one lifetime, a reference counted
-- resource associates each *reference* with a lifetime, and is released when
-- all references have expired.
module Lifetimes.Rc
( Rc
, addRef
, refCounted
) where
import Control.Concurrent.STM
import Lifetimes
import Zhp
-- | A resource which is managed by reference counting.
data Rc a = Rc
{ count :: TVar Int
, value :: a
, cleanup :: IO ()
}
-- | Acquire a new reference.
addRef :: Rc a -> Acquire a
addRef rc =
mkAcquire
(atomically $ incRef rc)
(\_ -> join $ atomically $ decRef rc)
resourceToRc :: Resource a -> STM (Rc a)
resourceToRc res = do
value <- mustGetResource res
cleanup <- detach res
count <- newTVar 1
pure Rc { count, cleanup, value }
-- | Acquire a resource using refcounting. Takes an 'Acquire' for the underlying
-- resource, and returns one that acquires an initial reference to it. Additional
-- references may be created using 'addRef', and the underlying resource will be
-- kept alive until all resources are released.
refCounted :: Acquire a -> Acquire (Rc a)
refCounted acq = do
lt <- currentLifetime
liftIO $ withLifetime $ \tmpLt -> do
res <- acquire tmpLt acq
acquireValue lt $ mkAcquire
(atomically $ resourceToRc res)
(join . atomically . decRef)
incRef :: Rc a -> STM a
incRef Rc{count, value} = do
modifyTVar' count succ
pure value
decRef :: Rc a -> STM (IO ())
decRef Rc{count, cleanup} = do
modifyTVar' count pred
c <- readTVar count
pure $ case c of
0 -> cleanup
_ -> pure ()