dao-0.1.0.1: src/Dao/RefTable.hs
-- "src/Dao/RefTable.hs" a data type which maps opaque reference
-- handles to 'Dynamic' objects.
--
-- Copyright (C) 2008-2014 Ramin Honary.
-- This file is part of the Dao System.
--
-- The Dao System is free software: you can redistribute it and/or
-- modify it under the terms of the GNU General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.
--
-- The Dao System is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program (see the file called "LICENSE"). If not, see
-- <http://www.gnu.org/licenses/agpl.html>.
-- | This module is used to implement a kind of un-typed global state for the Dao runtime which can
-- store object of arbitrary type in a place that can be accessed anywhere. It is used for data
-- types like file handles which have their own unique identifiers and you don't need Dao to assign
-- it a new unique ID to it them for you, you can just index it by a hash of the the unique ID.
--
-- Every value stored in a table gets it's own MVar, so updating the value is thread safe, but it is
-- a bit memory intensive. So a 'RefTable' should only be used for things that really need to be
-- well protected, like UNIX file descriptors or sockets, ordinary file handles, credentials,
-- handles to access databases -- anything that needs to be allocated and released in order for it
-- to be used during runtime.
module Dao.RefTable
( RefMonad,
RefNode, refNodeDestructor, refNodeValue, refTableNode,
RefTable, newRefTable, initializeWithKey, destroyWithKey
)
where
import qualified Dao.HashMap as H
import Control.Applicative
import Control.Concurrent.MVar
import Control.Monad.Reader
----------------------------------------------------------------------------------------------------
type RefMonad key val a = ReaderT (RefTable key val) IO a
data RefNode a = RefNode{ refNodeDestructor :: IO (), refNodeValue :: MVar a }
refTableNode :: (a -> IO ()) -> a -> IO (RefNode a)
refTableNode destructor a = newMVar a >>= \mvar ->
return $ RefNode{ refNodeDestructor=destructor a, refNodeValue=mvar }
newtype RefTable key a = RefTable { refTableMVar :: MVar (H.HashMap key (RefNode a)) }
newRefTable :: IO (RefTable key a)
newRefTable = RefTable <$> newMVar H.empty
initializeWithKey :: Ord key => a -> IO () -> H.Index key -> RefMonad key a (MVar a)
initializeWithKey o destructor key = do
mvar <- H.hashLookup key <$> (asks refTableMVar >>= liftIO . readMVar)
case mvar of
Just node -> return (refNodeValue node)
Nothing -> do
mvar <- liftIO $ newMVar o
asks refTableMVar >>=
liftIO . flip modifyMVar (\hmap -> return (H.hashInsert key (RefNode destructor mvar) hmap, mvar))
destroyWithKey :: Ord key => H.Index key -> RefMonad key a ()
destroyWithKey key = do
mvar <- H.hashLookup key <$> (asks refTableMVar >>= liftIO . readMVar)
case mvar of
Nothing -> return ()
Just _ -> asks refTableMVar >>= liftIO . flip modifyMVar_ (return . H.hashDelete key)