----------------------------------------------------------------------------- -- | -- Module : Network.Transport.Memory -- Copyright : (c) Phil Hargett 2013 -- License : MIT (see LICENSE file) -- -- Maintainer : phil@haphazardhouse.net -- Stability : experimental -- Portability : non-portable (requires STM) -- -- Memory transports deliver messages to other 'Network.Endpoints.Endpoint's within the same shared -- address space, or operating system process. -- Internally memory transports use a set of 'TQueue's to deliver messages to 'Network.Endpoint.Endpoint's. -- Memory transports are not global in nature: 'Network.Endpoint.Endpoint's can only communicate with -- one another if each has added the same memory 'Transport' and each invoked 'bind' on that shared -- transport. -- ----------------------------------------------------------------------------- module Network.Transport.Memory ( newMemoryTransport, module Network.Transport ) where -- local imports import Control.Concurrent.Mailbox import Network.Endpoints import Network.Transport -- external imports import Control.Concurrent.Async import Control.Concurrent.STM import Control.Exception import qualified Data.Map as M -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- {-| Create a new memory 'Transport' for use by 'Network.Endpoint.Endpoint's. -} newMemoryTransport :: IO Transport newMemoryTransport = do vBindings <- atomically $ newTVar M.empty return Transport { bind = memoryBind vBindings, dispatch = memoryDispatcher vBindings, connect = memoryConnect, shutdown = return () } memoryDispatcher :: TBindings -> Endpoint -> IO Dispatcher memoryDispatcher vBindings endpoint = do d <- async disp return Dispatcher { stop = do cancel d memoryFlushMessages vBindings endpoint } where disp = do atomically $ do bindings <- readTVar vBindings env <- selectMailbox (endpointOutbound endpoint) $ \envelope -> case M.lookup (messageDestination envelope) bindings of Just _ -> Just envelope _ -> Nothing memoryDispatchEnvelope bindings env disp memoryDispatchEnvelope :: Bindings -> Envelope -> STM () memoryDispatchEnvelope bindings env = case M.lookup (messageDestination env) bindings of Nothing -> return () Just destination -> postMessage destination (envelopeMessage env) memoryBind :: TBindings -> Endpoint -> Name -> IO Binding memoryBind vBindings endpoint name = atomically $ do bindings <- readTVar vBindings case M.lookup name bindings of Nothing -> do modifyTVar vBindings $ M.insert name endpoint return Binding { bindingName = name, unbind = memoryUnbind vBindings endpoint name } Just _ -> throw $ BindingExists name memoryUnbind :: TBindings -> Endpoint -> Name -> IO () memoryUnbind vBindings _ name = atomically $ modifyTVar vBindings $ M.delete name type TBindings = TVar Bindings type Bindings = M.Map Name Endpoint memoryConnect :: Endpoint -> Name -> IO Connection memoryConnect _ _ = return Connection { disconnect = return () } memoryFlushMessages :: TBindings -> Endpoint -> IO () memoryFlushMessages vBindings endpoint = atomically flush where flush = do bindings <- readTVar vBindings maybeEnv <- tryReadMailbox $ endpointOutbound endpoint case maybeEnv of Just env -> do memoryDispatchEnvelope bindings env flush Nothing -> return ()