Find file
Fetching contributors…
Cannot retrieve contributors at this time
77 lines (68 sloc) 3.97 KB
-- | Exposes a high-level interface for starting a node of a distributed
-- program, taking into account a local configuration file, command
-- line arguments, and commonly-used system processes.
module Remote.Init (remoteInit,remoteInitFromConfig) where
import Remote.Peer (startDiscoveryService)
import Remote.Task (__remoteCallMetaData)
import Remote.Process (startProcessRegistryService,suppressTransmitException,pbracket,localRegistryRegisterNode,localRegistryHello,localRegistryUnregisterNode,
import Remote.Reg (registerCalls,RemoteCallMetaData)
import System.FilePath (FilePath)
import System.Environment (getEnvironment)
import Control.Concurrent (threadDelay)
import Control.Monad.Trans (liftIO)
import Control.Exception (finally)
import Control.Concurrent.MVar (MVar,takeMVar,putMVar,newEmptyMVar)
startServices :: ProcessM ()
startServices =
startFinalizerService (suppressTransmitException localRegistryUnregisterNode >> return ())
dispatchServices :: MVar Node -> IO ()
dispatchServices node = do mv <- newEmptyMVar
runLocalProcess node (startServices >> liftIO (putMVar mv ()))
takeMVar mv
remoteInitFromConfig :: Config -> [RemoteCallMetaData] -> (String -> ProcessM ()) -> IO ()
remoteInitFromConfig cfg metadata f =
defaultMetaData = [Remote.Task.__remoteCallMetaData]
lookup = registerCalls (defaultMetaData ++ metadata)
node <- initNode cfg lookup
_ <- startLocalRegistry cfg False -- potentially fails silently
forkAndListenAndDeliver node cfg
dispatchServices node
(roleDispatch node userFunction >> waitForThreads node) `finally` (performFinalization node)
threadDelay 500000 -- TODO make configurable, or something
where userFunction s = localRegistryHello >> localRegistryRegisterNode >> f s
-- | This is the usual way create a single node of distributed program.
-- The intent is that 'remoteInit' be called in your program's 'Main.main'
-- function. A typical call takes this form:
-- > main = remoteInit (Just "config") [Main.__remoteCallMetaData] initialProcess
-- This will:
-- 1. Read the configuration file @config@ in the current directory or, if specified, from the file whose path is given by the environment variable @RH_CONFIG@. If the given file does not exist or is invalid, an exception will be thrown.
-- 2. Use the configuration given in the file as well as on the command-line to create a new node. The usual system processes will be started, including logging, discovery, and spawning.
-- 3. Compile-time metadata, generated by 'Remote.Call.remotable', will used for invoking closures. Metadata from each module must be explicitly mentioned.
-- 4. The function initialProcess will be called, given as a parameter a string indicating the value of the cfgRole setting of this node. initialProcess is provided by the user and provides an entrypoint for controlling node behavior on startup.
remoteInit :: Maybe FilePath -> [RemoteCallMetaData] -> (String -> ProcessM ()) -> IO ()
remoteInit defaultConfig metadata f =
configFileName <- getConfigFileName
cfg <- readConfig True configFileName
-- TODO sanity-check cfg
remoteInitFromConfig cfg metadata f
where getConfigFileName = do env <- getEnvironment
return $ maybe defaultConfig Just (lookup "RH_CONFIG" env)