-- | A simple reference counter
module Util.ReferenceCount(
   RefCount,

   newRefCount, -- :: IO RefCount
      -- new ref count with no links.
   newLinkedRefCount, -- :: IO RefCount
      -- new ref count with one link (that being what is normally wanted).
   addRef, -- :: RefCount -> IO ()
   remRef, -- :: RefCount -> IO Bool
      -- returns True if we reach 0.
   ) where

import Control.Concurrent.MVar

newtype RefCount = RefCount (MVar Int)


newRefCount :: IO RefCount
newRefCount :: IO RefCount
newRefCount =
   do
      MVar Int
mVar <- Int -> IO (MVar Int)
forall a. a -> IO (MVar a)
newMVar Int
0
      RefCount -> IO RefCount
forall (m :: * -> *) a. Monad m => a -> m a
return (MVar Int -> RefCount
RefCount MVar Int
mVar)

newLinkedRefCount :: IO RefCount
newLinkedRefCount :: IO RefCount
newLinkedRefCount =
   do
      MVar Int
mVar <- Int -> IO (MVar Int)
forall a. a -> IO (MVar a)
newMVar Int
1
      RefCount -> IO RefCount
forall (m :: * -> *) a. Monad m => a -> m a
return (MVar Int -> RefCount
RefCount MVar Int
mVar)

addRef :: RefCount -> IO ()
addRef :: RefCount -> IO ()
addRef (RefCount MVar Int
mVar) = MVar Int -> (Int -> IO Int) -> IO ()
forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ MVar Int
mVar (Int -> IO Int
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> IO Int) -> (Int -> Int) -> Int -> IO Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1))

remRef :: RefCount -> IO Bool
remRef :: RefCount -> IO Bool
remRef (RefCount MVar Int
mVar) = MVar Int -> (Int -> IO (Int, Bool)) -> IO Bool
forall a b. MVar a -> (a -> IO (a, b)) -> IO b
modifyMVar MVar Int
mVar (\ Int
count0 ->
   let
      count1 :: Int
count1 = Int
count0 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
   in
      (Int, Bool) -> IO (Int, Bool)
forall (m :: * -> *) a. Monad m => a -> m a
return (Int
count1,Int
count1 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0)
   )