@@ -1,18 +1,20 @@
1-- | Simple backend based on the TCP transport which offers node discovery
1-- | Simple backend based on the TCP transport which offers node discovery
2-- based on UDP multicast
2-- based on UDP multicast
3+{-# OPTIONS_GHC -fno-warn-orphans #-}
3module Control.Distributed.Process.Backend.Local
4module Control.Distributed.Process.Backend.Local
4 ( LocalBackend(..)
5 ( LocalBackend(..)
5 , initializeBackend
6 , initializeBackend
8−import Data.Binary (encode)
9+import Data.Binary (Binary(get, put), getWord8, putWord8)
9−import qualified Data.ByteString as BSS (concat, append)
10+import Data.Accessor (Accessor, accessor, (^:), (^.))
10−import qualified Data.ByteString.Lazy as BSL (toChunks, length)
12+import qualified Data.Set as Set (insert, empty, toList)
11import Control.Applicative ((<$>))
13import Control.Applicative ((<$>))
12import Control.Exception (throw)
14import Control.Exception (throw)
13−import Control.Monad (forM_)
15+import Control.Monad (forM_, forever)
14−import Control.Concurrent (forkIO)
16+import Control.Concurrent (forkIO, threadDelay)
15−import Control.Concurrent.MVar (MVar, newMVar, readMVar)
17+import Control.Concurrent.MVar (MVar, newMVar, readMVar, modifyMVar_)
16import Control.Distributed.Process.Internal.Types
18import Control.Distributed.Process.Internal.Types
17 ( LocalNode
19 ( LocalNode
18 , RemoteTable
20 , RemoteTable
@@ -26,55 +28,97 @@ import qualified Network.Transport.TCP as NT
26 ( createTransport
28 ( createTransport
27 , defaultTCPParameters
29 , defaultTCPParameters
29−import Network.Transport.Internal (encodeInt32, decodeInt32)
30−import qualified Network.Transport as NT ()
31+import qualified Network.Transport as NT (Transport)
31−import qualified Network.Socket as N (HostName, ServiceName, Socket, SockAddr)
32+import qualified Network.Socket as N (HostName, ServiceName, SockAddr)
32−import qualified Network.Socket.ByteString as NBS (recvFrom, sendTo)
33+import Control.Distributed.Process.Internal.Multicast (initMulticast)
33−import qualified Network.Multicast as NM (multicastSender, multicastReceiver)
35data LocalBackend = LocalBackend {
36data LocalBackend = LocalBackend {
37+ -- | Create a new local node
36 newLocalNode :: IO LocalNode
38 newLocalNode :: IO LocalNode
39+ -- | @findPeers t@ sends out a /who's there?/ request, waits 't' msec,
40+ -- and then collects and returns the answers
37− , findPeers :: IO [NodeId]
41+ , findPeers :: Int -> IO [NodeId]
40data LocalBackendState = LocalBackendState {
44data LocalBackendState = LocalBackendState {
41− localNodes :: [LocalNode]
45+ _localNodes :: [LocalNode]
46+ , _peers :: Set NodeId
44-- | Initialize the backend
49-- | Initialize the backend
45initializeBackend :: N.HostName -> N.ServiceName -> RemoteTable -> IO LocalBackend
50initializeBackend :: N.HostName -> N.ServiceName -> RemoteTable -> IO LocalBackend
46initializeBackend host port rtable = do
51initializeBackend host port rtable = do
47− mTransport <- NT.createTransport host port NT.defaultTCPParameters
52+ mTransport <- NT.createTransport host port NT.defaultTCPParameters
48− mcastSender <- NM.multicastSender "224.0.0.99" 9999
53+ (recv, send) <- initMulticast "224.0.0.99" 9999 1024
49− mcastReceiver <- NM.multicastReceiver "224.0.0.99" 9999
50− backendState <- newMVar LocalBackendState { localNodes = [] }
54+ backendState <- newMVar LocalBackendState
51− forkIO $ peerDiscoveryDaemon backendState mcastReceiver mcastSender
58+ forkIO $ peerDiscoveryDaemon backendState recv send
52 case mTransport of
59 case mTransport of
53 Left err -> throw err
60 Left err -> throw err
54 Right transport -> return LocalBackend {
61 Right transport -> return LocalBackend {
55− newLocalNode = Node.newLocalNode transport rtable
62+ newLocalNode = apiNewLocalNode transport rtable backendState
56− , findPeers = apiFindPeers mcastSender
63+ , findPeers = apiFindPeers send backendState
66+-- | Create a new local node
67+apiNewLocalNode :: NT.Transport
69+ -> MVar LocalBackendState
71+apiNewLocalNode transport rtable backendState = do
72+ localNode <- Node.newLocalNode transport rtable
73+ modifyMVar_ backendState $ return . (localNodes ^: (localNode :))
59-- | Peer discovery
76-- | Peer discovery
60−apiFindPeers :: (N.Socket, N.SockAddr) -> IO [NodeId]
77+apiFindPeers :: (PeerDiscoveryMsg -> IO ())
61−apiFindPeers (sock, addr) = do
78+ -> MVar LocalBackendState
62− NBS.sendTo sock (encodeInt32 (0xCDAECDAE :: Int)) addr
79+ -> Int
63− return []
80+ -> IO [NodeId]
81+apiFindPeers send backendState delay = do
82+ send PeerDiscoveryRequest
84+ Set.toList . (^. peers) <$> readMVar backendState
86+data PeerDiscoveryMsg =
88+ | PeerDiscoveryReply NodeId
90+instance Binary PeerDiscoveryMsg where
91+ put PeerDiscoveryRequest = putWord8 0
92+ put (PeerDiscoveryReply nid) = putWord8 1 >> put nid
96+ 0 -> return PeerDiscoveryRequest
97+ 1 -> PeerDiscoveryReply <$> get
98+ _ -> fail "PeerDiscoveryMsg.get: invalid"
65-- | Respond to peer discovery requests sent by other nodes
100-- | Respond to peer discovery requests sent by other nodes
66peerDiscoveryDaemon :: MVar LocalBackendState
101peerDiscoveryDaemon :: MVar LocalBackendState
68− -> (N.Socket, N.SockAddr)
102+ -> IO (PeerDiscoveryMsg, N.SockAddr)
103+ -> (PeerDiscoveryMsg -> IO ())
70−peerDiscoveryDaemon backendState readSock (writeSock, addr) = do
105+peerDiscoveryDaemon backendState recv send = forever go
71− (msgLen, _remoteAddr) <- NBS.recvFrom readSock 4
106+ where
110+ PeerDiscoveryRequest -> do
111+ nodes <- (^. localNodes) <$> readMVar backendState
112+ forM_ nodes $ send . PeerDiscoveryReply . Node.localNodeId
113+ PeerDiscoveryReply nid ->
114+ modifyMVar_ backendState $ return . (peers ^: Set.insert nid)
116+--------------------------------------------------------------------------------
118+--------------------------------------------------------------------------------
120+localNodes :: Accessor LocalBackendState [LocalNode]
121+localNodes = accessor _localNodes (\ns st -> st { _localNodes = ns })
73− case decodeInt32 msgLen of
123+peers :: Accessor LocalBackendState (Set NodeId)
74− (0xCDAECDAE :: Int) -> do
124+peers = accessor _peers (\ps st -> st { _peers = ps })
75− nodes <- localNodes <$> readMVar backendState
76− forM_ nodes $ \node -> do
77− let nodeAddr = encode (Node.localNodeId node)
78− msg = encodeInt32 (BSL.length nodeAddr) `BSS.append`
79− (BSS.concat . BSL.toChunks $ nodeAddr)
80− NBS.sendTo writeSock msg addr