vcache-0.1: hsrc_lib/Database/VCache/PVar.hs
module Database.VCache.PVar
( PVar
, newPVar
, newPVars
, newPVarIO
, newPVarsIO
, loadRootPVar
, loadRootPVarIO
, readPVar
, readPVarIO
, writePVar
, modifyPVar
, modifyPVar'
, swapPVar
, pvar_space
, unsafePVarAddr
, unsafePVarRefct
) where
import Control.Concurrent.STM
import Database.VCache.Types
import Database.VCache.Alloc ( newPVar, newPVars, newPVarIO, newPVarsIO
, loadRootPVar, loadRootPVarIO)
import Database.VCache.Read (readRefctIO)
-- | Read a PVar as part of a transaction.
readPVar :: PVar a -> VTx a
readPVar pvar =
getVTxSpace >>= \ space ->
if (space /= pvar_space pvar) then fail eBadSpace else
liftSTM $ readTVar (pvar_data pvar) >>= \ rdv ->
case rdv of { (RDV v) -> return v }
{-# INLINABLE readPVar #-}
-- Note that readPVar and readPVarIO must be strict in RDV in order to force
-- the initial, lazy read from the database. This is the only reason for RDV.
-- Without forcing here, a lazy read might return a value from an update.
-- | Read a PVar in the IO monad.
--
-- This is more efficient than a full transaction. It simply peeks at
-- the underlying TVar with readTVarIO. Durability of the value read
-- is not guaranteed.
readPVarIO :: PVar a -> IO a
readPVarIO pv =
readTVarIO (pvar_data pv) >>= \ rdv ->
case rdv of { (RDV v) -> return v }
{-# INLINE readPVarIO #-}
eBadSpace :: String
eBadSpace = "VTx: mismatch between VTx VSpace and PVar VSpace"
-- | Write a PVar as part of a transaction.
writePVar :: PVar a -> a -> VTx ()
writePVar pvar v =
getVTxSpace >>= \ space ->
if (space /= pvar_space pvar) then fail eBadSpace else
markForWrite pvar v >>
liftSTM (writeTVar (pvar_data pvar) (RDV v))
{-# INLINABLE writePVar #-}
-- | Modify a PVar.
modifyPVar :: PVar a -> (a -> a) -> VTx ()
modifyPVar var f = do
x <- readPVar var
writePVar var (f x)
{-# INLINE modifyPVar #-}
-- | Modify a PVar, strictly.
modifyPVar' :: PVar a -> (a -> a) -> VTx ()
modifyPVar' var f = do
x <- readPVar var
writePVar var $! f x
{-# INLINE modifyPVar' #-}
-- | Swap contents of a PVar for a new value.
swapPVar :: PVar a -> a -> VTx a
swapPVar var new = do
old <- readPVar var
writePVar var new
return old
{-# INLINE swapPVar #-}
-- | Each PVar has a stable address in the VCache. This address will
-- be very stable, but is not deterministic and isn't really something
-- you should treat as meaningful information about the PVar. Mostly,
-- this function exists to support hashtables or memoization with
-- PVar keys.
--
-- The Show instance for PVars will also show the address.
unsafePVarAddr :: PVar a -> Address
unsafePVarAddr = pvar_addr
{-# INLINE unsafePVarAddr #-}
-- | This function allows developers to access the reference count
-- for the PVar that is currently recorded in the database. This may
-- be useful for heuristic purposes. However, caveats are needed:
--
-- First, because the VCache writer operates in a background thread,
-- the reference count returned here may be slightly out of date.
--
-- Second, it is possible that VCache will eventually use some other
-- form of garbage collection than reference counting. This function
-- should be considered an unstable element of the API.
--
-- Root PVars start with one root reference.
unsafePVarRefct :: PVar a -> IO Int
unsafePVarRefct var = readRefctIO (pvar_space var) (pvar_addr var)