-- | InfoBus implements the 'shutdown' command. This destroys all the -- things registered via 'registerTool' and not -- subsequently registered via 'deregisterTool'. Tools are identified -- by 'ObjectId'. module Reactor.InfoBus ( registerTool, registerToolDebug, deregisterTool, shutdown, registerDestroyAct, encapsulateWaitTermAct, ) where import Control.Exception import Control.Concurrent.MVar import qualified Data.Map as Map import System.IO.Unsafe import System.Mem(performGC) import Util.Computation import Util.Object import Util.Debug(debug) import Events.Destructible -- -------------------------------------------------------------------------- -- Tool Manager State -- -------------------------------------------------------------------------- type ToolManager = MVar Tools type Tools = Map.Map ObjectID (IO ()) -- -------------------------------------------------------------------------- -- Fetch Tool Manager State -- -------------------------------------------------------------------------- toolmanager :: ToolManager toolmanager = unsafePerformIO (newMVar Map.empty) {-# NOINLINE toolmanager #-} -- -------------------------------------------------------------------------- -- Client Commands -- -------------------------------------------------------------------------- registerTool :: (Object t, Destroyable t) => t -> IO () registerTool t = do map <- takeMVar toolmanager putMVar toolmanager (Map.insert (objectID t) (destroy t) map) done registerToolDebug :: (Object t, Destroyable t) => String -> t -> IO () registerToolDebug title t = do map <- takeMVar toolmanager putMVar toolmanager (Map.insert (objectID t) (destroy t) map) debug ("registerTool " ++ title,objectID t) done deregisterTool :: (Object t) => t -> IO () deregisterTool t = do let oid = objectID t try( -- ignore exceptions if they occur. I don't see how they can -- actually. do map <- takeMVar toolmanager putMVar toolmanager (Map.delete oid map) ) :: IO (Either SomeException ()) debug ("deregisterTool ",oid) done shutdown :: IO () shutdown = do map <- takeMVar toolmanager let toShutDown = Map.toList map putMVar toolmanager Map.empty foreach toShutDown (\ (oid,cmd) -> do debug ("Shutting down ",oid) try cmd :: IO (Either SomeException ()) ) performGC -- -------------------------------------------------------------------------- -- Simple interface allowing us to register something to be done without -- having to create special instances for it. -- -------------------------------------------------------------------------- -- | register the given action to be done at shutdown. The returned action -- cancels the registration (without performing the given action). registerDestroyAct :: IO () -> IO (IO ()) registerDestroyAct act = do oID <- newObject let simpleTool = SimpleTool { oID = oID, destroyAct = act } registerTool simpleTool return (deregisterTool simpleTool) -- | encapsulate an action such that shutdown waits for its termination encapsulateWaitTermAct :: IO () -> IO () encapsulateWaitTermAct act = do sync <- newEmptyMVar _ <- registerDestroyAct (readMVar sync) act putMVar sync () data SimpleTool = SimpleTool { oID :: ObjectID, destroyAct :: IO () } instance Object SimpleTool where objectID simpleTool = oID simpleTool instance Destroyable SimpleTool where destroy simpleTool = destroyAct simpleTool