module Caramia.Internal.ContextLocalData
( storeContextLocalData
, retrieveContextLocalData
, currentContextID
, nextContextID
, runningContexts
, contextLocalData
, ContextID )
where
import Caramia.Prelude
import qualified Data.IntMap.Strict as IM
import qualified Data.Map.Strict as M
import System.IO.Unsafe
import Data.Dynamic
import Control.Concurrent
type ContextID = Int
runningContexts :: IORef (M.Map ThreadId ContextID)
runningContexts = unsafePerformIO $ newIORef M.empty
nextContextID :: IORef ContextID
nextContextID = unsafePerformIO $ newIORef 0
contextLocalData :: IORef (IM.IntMap (M.Map TypeRep Dynamic))
contextLocalData = unsafePerformIO $ newIORef IM.empty
currentContextID :: IO (Maybe ContextID)
currentContextID =
M.lookup <$> myThreadId <*> readIORef runningContexts
storeContextLocalData :: Typeable a => a -> IO ()
storeContextLocalData value =
maybe (error "storeContextLocalData: not in a context.")
(\cid ->
atomicModifyIORef' contextLocalData $ \old ->
( IM.alter (Just . maybe (M.singleton
(typeOf value)
(toDyn value))
(M.insert (typeOf value)
(toDyn value)))
cid
old
, () ) )
=<< currentContextID
retrieveContextLocalData :: forall a. Typeable a
=> IO a
-> IO a
retrieveContextLocalData defvalue =
maybe (error "retrieveContextLocalData: not in a context.")
(\cid -> do
snapshot <- readIORef contextLocalData
case IM.lookup cid snapshot of
Nothing -> do
val <- dyndefvalue
atomicModifyIORef' contextLocalData $ \old ->
( IM.insert cid (M.singleton typ val) old
, fromDyn val undefined )
Just old_map ->
case M.lookup typ old_map of
Nothing -> do
val <- dyndefvalue
atomicModifyIORef' contextLocalData $ \old ->
( IM.adjust (M.insert typ val)
cid
old
, fromDyn val undefined )
Just value -> return (fromDyn value undefined))
=<< currentContextID
where
typ = typeOf (undefined :: a)
dyndefvalue = toDyn <$> defvalue