@@ -46,7 +46,7 @@ import qualified Network.Socket as N ( HostName
48import Network.Socket.ByteString (sendMany)
48import Network.Socket.ByteString (sendMany)
49−import Control.Concurrent (forkIO, ThreadId)
49+import Control.Concurrent (forkIO, ThreadId, killThread)
50import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
50import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan)
51import Control.Concurrent.MVar ( MVar
51import Control.Concurrent.MVar ( MVar
@@ -58,13 +58,13 @@ import Control.Concurrent.MVar ( MVar
58 , newEmptyMVar
58 , newEmptyMVar
59 , withMVar
59 , withMVar
61−import Data.IORef (IORef, newIORef, writeIORef, readIORef, modifyIORef)
62import Control.Category ((>>>))
61import Control.Category ((>>>))
63import Control.Applicative ((<*>), (*>), (<$>))
62import Control.Applicative ((<*>), (*>), (<$>))
64import Control.Monad (forM_, void, when)
63import Control.Monad (forM_, void, when)
65import Control.Monad.Error (ErrorT(ErrorT), runErrorT, throwError)
64import Control.Monad.Error (ErrorT(ErrorT), runErrorT, throwError)
66import Control.Monad.IO.Class (liftIO)
65import Control.Monad.IO.Class (liftIO)
67−import Control.Exception (IOException)
66+import Control.Exception (IOException, SomeException)
67+import Data.IORef (IORef, newIORef, writeIORef, readIORef, modifyIORef)
68import Data.ByteString (ByteString)
68import Data.ByteString (ByteString)
69import qualified Data.ByteString as BS (concat)
69import qualified Data.ByteString as BS (concat)
70import qualified Data.ByteString.Char8 as BSC (pack, unpack)
70import qualified Data.ByteString.Char8 as BSC (pack, unpack)
@@ -183,15 +183,18 @@ data LocalEndPoint = LocalEndPoint { localAddress :: EndPointAddress
184data RemoteEndPoint = RemoteEndPoint { remoteAddress :: EndPointAddress
184data RemoteEndPoint = RemoteEndPoint { remoteAddress :: EndPointAddress
185 , remoteState :: MVar RemoteState
185 , remoteState :: MVar RemoteState
186+ , remoteThread :: ThreadId
188-- We use underscores for fields that we might update (using lenses).
189-- We use underscores for fields that we might update (using lenses).
190−data TransportState = TransportState { _localEndPoints :: IntMap LocalEndPoint }
191+data TransportState = TransportState { _localEndPoints :: IntMap LocalEndPoint
192+ , _acceptNewConnections :: Bool
191−data LocalState = LocalState { _nextConnectionId :: ConnectionId
194+data LocalState = LocalState { _nextConnectionId :: ConnectionId
192− , _pendingCtrlRequests :: IntMap (MVar [ByteString])
195+ , _pendingCtrlRequests :: IntMap (MVar [ByteString])
193− , _nextCtrlRequestId :: ControlRequestId
196+ , _nextCtrlRequestId :: ControlRequestId
194− , _localConnections :: Map EndPointAddress RemoteEndPoint
197+ , _localConnections :: Map EndPointAddress RemoteEndPoint
197-- A remote endpoint has incoming and outgoing connections, and when the total
200-- A remote endpoint has incoming and outgoing connections, and when the total
@@ -220,15 +223,15 @@ data LocalState = LocalState { _nextConnectionId :: ConnectionId
220-- the writes for control messages).
223-- the writes for control messages).
222-- Of course, "serialize" does not mean that we want for the remote endpoint to
225-- Of course, "serialize" does not mean that we want for the remote endpoint to
223−-- reply. "Send" takes the mvar, sends to the endpoint, and then puts the mvar
226+-- reply. "Send" takes the mvar, sends to the endpoint (asynchronously), and
224−-- back, without waiting for the endpoint to receive the message. Similarly,
227+-- then puts the mvar back, without waiting for the endpoint to receive the
225−-- when requesting a new connection, we take the mvar, tentatively increment the
228+-- message. Similarly, when requesting a new connection, we take the mvar,
226−-- reference count, send the control request, and then put the mvar back. When
229+-- tentatively increment the reference count, send the control request, and
227−-- the remote host responds to the new connection request we might have to do
230+-- then put the mvar back. When the remote host responds to the new connection
228−-- another state change (reduce the refcount) if the connection request was
231+-- request we might have to do another state change (reduce the refcount) if
229−-- refused but we don't want to increment the ref count only after the remote
232+-- the connection request was refused but we don't want to increment the ref
230−-- host acknowledges the request because then a concurrent 'close' might
233+-- count only after the remote host acknowledges the request because then a
231−-- actually attempt to close the socket.
234+-- concurrent 'close' might actually attempt to close the socket.
233-- Since we don't do concurrent reads from the same socket we don't need to
236-- Since we don't do concurrent reads from the same socket we don't need to
234-- take the lock when reading from the socket.
237-- take the lock when reading from the socket.
@@ -270,14 +273,32 @@ data ConnectionRequestResponse =
270-- TODOs: deal with hints
273-- TODOs: deal with hints
271createTransport :: N.HostName -> N.ServiceName -> IO (Either IOException Transport)
274createTransport :: N.HostName -> N.ServiceName -> IO (Either IOException Transport)
272createTransport host port = do
275createTransport host port = do
273− state <- newMVar TransportState { _localEndPoints = IntMap.empty }
276+ state <- newMVar TransportState { _localEndPoints = IntMap.empty
277+ , _acceptNewConnections = True
274− let transport = TCPTransport { transportState = state
279+ let transport = TCPTransport { transportState = state
275− , transportHost = host
280+ , transportHost = host
276− , transportPort = port
281+ , transportPort = port
278− tryIO $ do
283+ tryIO $ do
284+ -- TODO: "5" is listed as "system dependent maximum backlog"?
279− forkServer host port $ void . handleConnectionRequest transport
285+ transportThread <- forkServer host port 5 (handleConnectionRequest transport)
286+ (terminationHandler transport)
280− return Transport { newEndPoint = apiNewEndPoint transport }
287+ return Transport { newEndPoint = apiNewEndPoint transport
288+ , closeTransport = apiCloseTransport transportThread
291+ terminationHandler :: TCPTransport -> SomeException -> IO ()
292+ terminationHandler transport _ =
293+ modifyMVar_ (transportState transport) $ return . (acceptNewConnections ^= False)
295+-- | Shut down the transport
296+apiCloseTransport :: ThreadId -> IO ()
297+apiCloseTransport transportThread = do
298+ -- Kill the thread that is listening for new incoming connections
299+ killThread transportThread
300+ -- Not yet implemented
282--------------------------------------------------------------------------------
303--------------------------------------------------------------------------------
283-- API functions --
304-- API functions --
@@ -593,20 +614,18 @@ decreaseRemoteRefCount theirEndPoint = do
594-- | Handle a connection request (that is, a remote endpoint that is trying to
615-- | Handle a connection request (that is, a remote endpoint that is trying to
595-- establish a TCP connection with us)
616-- establish a TCP connection with us)
597−-- Returns the thread ID of the thread that is handling incoming messages.
598−handleConnectionRequest :: TCPTransport -> N.Socket -> IO (Maybe ThreadId)
617+handleConnectionRequest :: TCPTransport -> N.Socket -> IO ()
599handleConnectionRequest transport sock = do
618handleConnectionRequest transport sock = do
619+ let tState = transportState transport
600 request <- tryIO $ do
620 request <- tryIO $ do
601 ourEndPointId <- recvInt32 sock
621 ourEndPointId <- recvInt32 sock
602 theirAddress <- EndPointAddress . BS.concat <$> recvWithLength sock
622 theirAddress <- EndPointAddress . BS.concat <$> recvWithLength sock
603− ourEndPoint <- (^. localEndPointAt ourEndPointId) <$> readMVar (transportState transport)
623+ ourEndPoint <- (^. localEndPointAt ourEndPointId) <$> readMVar tState
604 return (theirAddress, ourEndPoint)
624 return (theirAddress, ourEndPoint)
605 case request of
625 case request of
606 Right (_, Nothing) -> do
626 Right (_, Nothing) -> do
607 sendMany sock [encodeInt32 ConnectionRequestEndPointInvalid]
627 sendMany sock [encodeInt32 ConnectionRequestEndPointInvalid]
608 N.sClose sock
628 N.sClose sock
610 Right (theirAddress, Just ourEndPoint) -> do
629 Right (theirAddress, Just ourEndPoint) -> do
611 (crossed, theirEndPoint) <- modifyMVar (localState ourEndPoint) $ \st ->
630 (crossed, theirEndPoint) <- modifyMVar (localState ourEndPoint) $ \st ->
612 case st ^. connectionTo theirAddress of
631 case st ^. connectionTo theirAddress of
@@ -622,7 +641,6 @@ handleConnectionRequest transport sock = do
623 sendMany sock [encodeInt32 ConnectionRequestCrossed]
642 sendMany sock [encodeInt32 ConnectionRequestCrossed]
624 N.sClose sock
643 N.sClose sock
627 let remoteConn = RemoteConnection { remoteSocket = sock
645 let remoteConn = RemoteConnection { remoteSocket = sock
628 , _remoteRefCount = 0
646 , _remoteRefCount = 0
@@ -630,12 +648,12 @@ handleConnectionRequest transport sock = do
631 putMVar (remoteState theirEndPoint) (RemoteEndPointValid remoteConn)
649 putMVar (remoteState theirEndPoint) (RemoteEndPointValid remoteConn)
632 sendMany sock [encodeInt32 ConnectionRequestAccepted]
650 sendMany sock [encodeInt32 ConnectionRequestAccepted]
651+ -- TODO: we should store the tid as part of the endpoint state
633 tid <- forkIO $ handleIncomingMessages (ourEndPoint, theirEndPoint)
652 tid <- forkIO $ handleIncomingMessages (ourEndPoint, theirEndPoint)
634− return (Just tid)
653+ return ()
635 Left _ -> do
654 Left _ -> do
636 -- Invalid request
655 -- Invalid request
637 N.sClose sock
656 N.sClose sock
640-- | Handle requests from a remote endpoint.
658-- | Handle requests from a remote endpoint.
@@ -794,6 +812,9 @@ firstNonReservedConnectionId = 1024
794localEndPoints :: Lens TransportState (IntMap LocalEndPoint)
812localEndPoints :: Lens TransportState (IntMap LocalEndPoint)
795localEndPoints = lens _localEndPoints (\es st -> st { _localEndPoints = es })
813localEndPoints = lens _localEndPoints (\es st -> st { _localEndPoints = es })
815+acceptNewConnections :: Lens TransportState Bool
816+acceptNewConnections = lens _acceptNewConnections (\acc st -> st { _acceptNewConnections = acc })
797pendingCtrlRequests :: Lens LocalState (IntMap (MVar [ByteString]))
818pendingCtrlRequests :: Lens LocalState (IntMap (MVar [ByteString]))
798pendingCtrlRequests = lens _pendingCtrlRequests (\rep st -> st { _pendingCtrlRequests = rep })
819pendingCtrlRequests = lens _pendingCtrlRequests (\rep st -> st { _pendingCtrlRequests = rep })