@@ -23,8 +23,10 @@ module Network.Transport.TCP
23 , decodeEndPointAddress
23 , decodeEndPointAddress
24 , ControlHeader(..)
24 , ControlHeader(..)
25 , ConnectionRequestResponse(..)
25 , ConnectionRequestResponse(..)
26− , firstNonReservedConnectionId
26+ , firstNonReservedLightweightConnectionId
27+ , firstNonReservedHeavyweightConnectionId
27 , socketToEndPoint
28 , socketToEndPoint
29+ , LightweightConnectionId
28 -- * Design notes
30 -- * Design notes
29 -- $design
31 -- $design
@@ -45,7 +47,6 @@ import Network.Transport.TCP.Internal
46import Network.Transport.Internal
48import Network.Transport.Internal
47 ( encodeInt32
49 ( encodeInt32
49 , prependLength
50 , prependLength
50 , mapIOException
51 , mapIOException
@@ -79,7 +80,6 @@ import Control.Concurrent.MVar
79 , modifyMVar
80 , modifyMVar
80 , modifyMVar_
81 , modifyMVar_
81 , readMVar
82 , readMVar
84 , newEmptyMVar
84 , newEmptyMVar
85 , withMVar
85 , withMVar
@@ -103,11 +103,10 @@ import Data.IORef (IORef, newIORef, writeIORef, readIORef)
103import Data.ByteString (ByteString)
103import Data.ByteString (ByteString)
104import qualified Data.ByteString as BS (concat)
104import qualified Data.ByteString as BS (concat)
105import qualified Data.ByteString.Char8 as BSC (pack, unpack)
105import qualified Data.ByteString.Char8 as BSC (pack, unpack)
106−import Data.Int (Int32)
106+import Data.Bits (shiftL, (.|.))
107−import Data.IntMap (IntMap)
107+import Data.Word (Word32)
108−import qualified Data.IntMap as IntMap (empty)
109−import Data.IntSet (IntSet)
108+import Data.Set (Set)
110−import qualified Data.IntSet as IntSet
109+import qualified Data.Set as Set
@@ -119,7 +118,7 @@ import qualified Data.IntSet as IntSet
119import Data.Map (Map)
118import Data.Map (Map)
120import qualified Data.Map as Map (empty)
119import qualified Data.Map as Map (empty)
121import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
120import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
122−import qualified Data.Accessor.Container as DAC (mapMaybe, intMapMaybe)
121+import qualified Data.Accessor.Container as DAC (mapMaybe)
123import Data.Foldable (forM_, mapM_)
122import Data.Foldable (forM_, mapM_)
125-- $design
124-- $design
@@ -256,9 +255,9 @@ data LocalEndPointState =
256 | LocalEndPointClosed
255 | LocalEndPointClosed
258data ValidLocalEndPointState = ValidLocalEndPointState
257data ValidLocalEndPointState = ValidLocalEndPointState
259− { _nextConnectionId :: !ConnectionId
258+ { _nextConnOutId :: !LightweightConnectionId
259+ , _nextConnInId :: !HeavyweightConnectionId
260 , _localConnections :: !(Map EndPointAddress RemoteEndPoint)
260 , _localConnections :: !(Map EndPointAddress RemoteEndPoint)
261− , _nextRemoteId :: !Int
264-- REMOTE ENDPOINTS
263-- REMOTE ENDPOINTS
@@ -278,14 +277,14 @@ data ValidLocalEndPointState = ValidLocalEndPointState
278-- \-------+----------+--------> Failed
277-- \-------+----------+--------> Failed
280-- Init: There are two places where we create new remote endpoints: in
279-- Init: There are two places where we create new remote endpoints: in
281−-- requestConnectionTo (in response to an API 'connect' call) and in
280+-- createConnectionTo (in response to an API 'connect' call) and in
282-- handleConnectionRequest (when a remote node tries to connect to us).
281-- handleConnectionRequest (when a remote node tries to connect to us).
283-- 'Init' carries an MVar () 'resolved' which concurrent threads can use to
282-- 'Init' carries an MVar () 'resolved' which concurrent threads can use to
284-- wait for the remote endpoint to finish initialization. We record who
283-- wait for the remote endpoint to finish initialization. We record who
285-- requested the connection (the local endpoint or the remote endpoint).
284-- requested the connection (the local endpoint or the remote endpoint).
287-- Invalid: We put the remote endpoint in invalid state only during
286-- Invalid: We put the remote endpoint in invalid state only during
288−-- requestConnectionTo when we fail to connect.
287+-- createConnectionTo when we fail to connect.
290-- Valid: This is the "normal" state for a working remote endpoint.
289-- Valid: This is the "normal" state for a working remote endpoint.
@@ -346,7 +345,7 @@ data ValidLocalEndPointState = ValidLocalEndPointState
346data RemoteEndPoint = RemoteEndPoint
345data RemoteEndPoint = RemoteEndPoint
347 { remoteAddress :: !EndPointAddress
346 { remoteAddress :: !EndPointAddress
348 , remoteState :: !(MVar RemoteState)
347 , remoteState :: !(MVar RemoteState)
349− , remoteId :: !Int
348+ , remoteId :: !HeavyweightConnectionId
350 , remoteScheduled :: !(Chan (IO ()))
349 , remoteScheduled :: !(Chan (IO ()))
@@ -368,35 +367,43 @@ data RemoteState =
368 -- using a closeTransport or closeEndPoint API call
367 -- using a closeTransport or closeEndPoint API call
369 | RemoteEndPointFailed !IOException
368 | RemoteEndPointFailed !IOException
370+-- TODO: we might want to replace Set (here and elsewhere) by faster
373+-- TODO: we could get rid of 'remoteIncoming' (and maintain less state) if
374+-- we introduce a new event 'AllConnectionsClosed'
371data ValidRemoteEndPointState = ValidRemoteEndPointState
375data ValidRemoteEndPointState = ValidRemoteEndPointState
372 { _remoteOutgoing :: !Int
376 { _remoteOutgoing :: !Int
373− , _remoteIncoming :: !IntSet
377+ , _remoteIncoming :: !(Set LightweightConnectionId)
378+ , _remoteMaxIncoming :: !LightweightConnectionId
374 , remoteSocket :: !N.Socket
379 , remoteSocket :: !N.Socket
375 , remoteSendLock :: !(MVar ())
380 , remoteSendLock :: !(MVar ())
376− , _pendingCtrlRequests :: !(IntMap (MVar (Either IOException [ByteString])))
377− , _nextCtrlRequestId :: !ControlRequestId
380-- | Local identifier for an endpoint within this transport
383-- | Local identifier for an endpoint within this transport
381−type EndPointId = Int32
384+type EndPointId = Word32
383−-- | Control request ID
385−-- Control requests are asynchronous; the request ID makes it possible to match
386−-- requests and replies
387−type ControlRequestId = Int32
389-- | Pair of local and a remote endpoint (for conciseness in signatures)
386-- | Pair of local and a remote endpoint (for conciseness in signatures)
390−type EndPointPair = (LocalEndPoint, RemoteEndPoint)
387+type EndPointPair = (LocalEndPoint, RemoteEndPoint)
389+-- | Lightweight connection ID (sender allocated)
391+-- A ConnectionId is the concentation of a 'HeavyweightConnectionId' and a
392+-- 'LightweightConnectionId'.
393+type LightweightConnectionId = Word32
395+-- | Heavyweight connection ID (recipient allocated)
397+-- A ConnectionId is the concentation of a 'HeavyweightConnectionId' and a
398+-- 'LightweightConnectionId'.
399+type HeavyweightConnectionId = Word32
392-- | Control headers
401-- | Control headers
393data ControlHeader =
402data ControlHeader =
394− -- | Request a new connection ID from the remote endpoint
403+ -- | Tell the remote endpoint that we created a new connection
395− RequestConnectionId
404+ CreatedNewConnection
396 -- | Tell the remote endpoint we will no longer be using a connection
405 -- | Tell the remote endpoint we will no longer be using a connection
397 | CloseConnection
406 | CloseConnection
398− -- | Respond to a control request /from/ the remote endpoint
400 -- | Request to close the connection (see module description)
407 -- | Request to close the connection (see module description)
401 | CloseSocket
408 | CloseSocket
402 deriving (Enum, Bounded, Show)
409 deriving (Enum, Bounded, Show)
@@ -558,7 +565,7 @@ apiConnect params ourEndPoint theirAddress _reliability hints =
559 resetIfBroken ourEndPoint theirAddress
566 resetIfBroken ourEndPoint theirAddress
560 (theirEndPoint, connId) <-
567 (theirEndPoint, connId) <-
561− requestConnectionTo params ourEndPoint theirAddress hints
568+ createConnectionTo params ourEndPoint theirAddress hints
562 -- connAlive can be an IORef rather than an MVar because it is protected
569 -- connAlive can be an IORef rather than an MVar because it is protected
563 -- by the remoteState MVar. We don't need the overhead of locking twice.
570 -- by the remoteState MVar. We don't need the overhead of locking twice.
564 connAlive <- newIORef True
571 connAlive <- newIORef True
@@ -568,7 +575,7 @@ apiConnect params ourEndPoint theirAddress _reliability hints =
570-- | Close a connection
577-- | Close a connection
571−apiClose :: EndPointPair -> ConnectionId -> IORef Bool -> IO ()
578+apiClose :: EndPointPair -> LightweightConnectionId -> IORef Bool -> IO ()
572apiClose (ourEndPoint, theirEndPoint) connId connAlive =
579apiClose (ourEndPoint, theirEndPoint) connId connAlive =
573 void . tryIO . asyncWhenCancelled return $ do
580 void . tryIO . asyncWhenCancelled return $ do
574 mAct <- modifyMVar (remoteState theirEndPoint) $ \st -> case st of
581 mAct <- modifyMVar (remoteState theirEndPoint) $ \st -> case st of
@@ -593,10 +600,10 @@ apiClose (ourEndPoint, theirEndPoint) connId connAlive =
595-- | Send data across a connection
602-- | Send data across a connection
596−apiSend :: EndPointPair -- ^ Local and remote endpoint
603+apiSend :: EndPointPair -- ^ Local and remote endpoint
597− -> ConnectionId -- ^ Connection ID (supplied by remote endpoint)
604+ -> LightweightConnectionId -- ^ Connection ID
598− -> IORef Bool -- ^ Is the connection still alive?
605+ -> IORef Bool -- ^ Is the connection still alive?
599− -> [ByteString] -- ^ Payload
606+ -> [ByteString] -- ^ Payload
600 -> IO (Either (TransportError SendErrorCode) ())
607 -> IO (Either (TransportError SendErrorCode) ())
601apiSend (ourEndPoint, theirEndPoint) connId connAlive payload =
608apiSend (ourEndPoint, theirEndPoint) connId connAlive payload =
602 -- We don't need the overhead of asyncWhenCancelled here
609 -- We don't need the overhead of asyncWhenCancelled here
@@ -665,7 +672,9 @@ apiCloseEndPoint transport evs ourEndPoint =
665 return (closed, Nothing)
672 return (closed, Nothing)
666 RemoteEndPointValid vst -> do
673 RemoteEndPointValid vst -> do
667 act <- schedule theirEndPoint $ do
674 act <- schedule theirEndPoint $ do
668− tryIO $ sendOn vst [encodeInt32 CloseSocket]
675+ tryIO $ sendOn vst [ encodeInt32 CloseSocket
676+ , encodeInt32 (vst ^. remoteMaxIncoming)
669 tryCloseSocket (remoteSocket vst)
678 tryCloseSocket (remoteSocket vst)
670 return (closed, Just act)
679 return (closed, Just act)
671 RemoteEndPointClosing resolved vst -> do
680 RemoteEndPointClosing resolved vst -> do
@@ -729,12 +738,11 @@ handleConnectionRequest transport sock = handle handleException $ do
730 sendLock <- newMVar ()
739 sendLock <- newMVar ()
731 let vst = ValidRemoteEndPointState
740 let vst = ValidRemoteEndPointState
732− { remoteSocket = sock
741+ { remoteSocket = sock
733− , remoteSendLock = sendLock
742+ , remoteSendLock = sendLock
734− , _remoteOutgoing = 0
743+ , _remoteOutgoing = 0
735− , _remoteIncoming = IntSet.empty
744+ , _remoteIncoming = Set.empty
736− , _pendingCtrlRequests = IntMap.empty
745+ , _remoteMaxIncoming = 0
737− , _nextCtrlRequestId = 0
739 sendMany sock [encodeInt32 ConnectionRequestAccepted]
747 sendMany sock [encodeInt32 ConnectionRequestAccepted]
740 resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointValid vst)
748 resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointValid vst)
@@ -790,32 +798,28 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
790 -- exception thrown by 'recv'.
798 -- exception thrown by 'recv'.
791 go :: N.Socket -> IO ()
799 go :: N.Socket -> IO ()
792 go sock = do
800 go sock = do
793− connId <- recvInt32 sock
801+ lcid <- recvInt32 sock :: IO LightweightConnectionId
794− if connId >= firstNonReservedConnectionId
802+ if lcid >= firstNonReservedLightweightConnectionId
796− readMessage sock connId
804+ readMessage sock lcid
799− case tryToEnum (fromIntegral connId) of
807+ case tryToEnum (fromIntegral lcid) of
800− Just RequestConnectionId -> do
808+ Just CreatedNewConnection -> do
801− recvInt32 sock >>= createNewConnection
809+ recvInt32 sock >>= createdNewConnection
803− Just ControlResponse -> do
804− recvInt32 sock >>= readControlResponse sock
806 Just CloseConnection -> do
811 Just CloseConnection -> do
807 recvInt32 sock >>= closeConnection
812 recvInt32 sock >>= closeConnection
809 Just CloseSocket -> do
814 Just CloseSocket -> do
810− didClose <- closeSocket sock
815+ didClose <- recvInt32 sock >>= closeSocket sock
811 unless didClose $ go sock
816 unless didClose $ go sock
812 Nothing ->
817 Nothing ->
813 throwIO $ userError "Invalid control request"
818 throwIO $ userError "Invalid control request"
815 -- Create a new connection
820 -- Create a new connection
816− createNewConnection :: ControlRequestId -> IO ()
821+ createdNewConnection :: LightweightConnectionId -> IO ()
817− createNewConnection reqId = do
822+ createdNewConnection lcid = do
818− newId <- getNextConnectionId ourEndPoint
819 modifyMVar_ theirState $ \st -> do
823 modifyMVar_ theirState $ \st -> do
820 vst <- case st of
824 vst <- case st of
821 RemoteEndPointInvalid _ ->
825 RemoteEndPointInvalid _ ->
@@ -825,73 +829,45 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
825 relyViolation (ourEndPoint, theirEndPoint)
829 relyViolation (ourEndPoint, theirEndPoint)
826 "handleIncomingMessages:createNewConnection (init)"
830 "handleIncomingMessages:createNewConnection (init)"
827 RemoteEndPointValid vst ->
831 RemoteEndPointValid vst ->
828− return (remoteIncoming ^: IntSet.insert newId $ vst)
832+ return ( (remoteIncoming ^: Set.insert lcid)
833+ $ (remoteMaxIncoming ^= lcid)
829 RemoteEndPointClosing resolved vst -> do
836 RemoteEndPointClosing resolved vst -> do
830 -- If the endpoint is in closing state that means we send a
837 -- If the endpoint is in closing state that means we send a
831 -- CloseSocket request to the remote endpoint. If the remote
838 -- CloseSocket request to the remote endpoint. If the remote
832− -- endpoint replies with the request to create a new connection, it
839+ -- endpoint replies that it created a new connection, it either
833− -- either ignored our request or it sent the request before it got
840+ -- ignored our request or it sent the request before it got ours.
834− -- ours. Either way, at this point we simply restore the endpoint
841+ -- Either way, at this point we simply restore the endpoint to
835− -- to RemoteEndPointValid
842+ -- RemoteEndPointValid
836 putMVar resolved ()
843 putMVar resolved ()
837− return (remoteIncoming ^= IntSet.singleton newId $ vst)
844+ return ( (remoteIncoming ^= Set.singleton lcid)
845+ . (remoteMaxIncoming ^= lcid)
838 RemoteEndPointFailed err ->
848 RemoteEndPointFailed err ->
839 throwIO err
849 throwIO err
840 RemoteEndPointClosed ->
850 RemoteEndPointClosed ->
841 relyViolation (ourEndPoint, theirEndPoint)
851 relyViolation (ourEndPoint, theirEndPoint)
842 "createNewConnection (closed)"
852 "createNewConnection (closed)"
843− sendOn vst ( encodeInt32 ControlResponse
845− : prependLength [encodeInt32 newId]
847 return (RemoteEndPointValid vst)
853 return (RemoteEndPointValid vst)
848− writeChan ourChannel (ConnectionOpened newId ReliableOrdered theirAddr)
854+ writeChan ourChannel (ConnectionOpened (connId lcid) ReliableOrdered theirAddr)
850− -- Read a control response
851− readControlResponse :: N.Socket -> ControlRequestId -> IO ()
852− readControlResponse sock reqId = do
853− response <- recvWithLength sock
854− mmvar <- modifyMVar theirState $ \st -> case st of
855− RemoteEndPointInvalid _ ->
856− relyViolation (ourEndPoint, theirEndPoint)
857− "readControlResponse (invalid)"
858− RemoteEndPointInit _ _ _ ->
859− relyViolation (ourEndPoint, theirEndPoint)
860− "readControlResponse (init)"
861− RemoteEndPointValid vst ->
862− return ( RemoteEndPointValid
863− . (pendingCtrlRequestsAt reqId ^= Nothing)
865− , vst ^. pendingCtrlRequestsAt reqId
867− RemoteEndPointClosing _ _ ->
868− throwIO $ userError "Invalid control response"
869− RemoteEndPointFailed err ->
871− RemoteEndPointClosed ->
872− relyViolation (ourEndPoint, theirEndPoint)
873− "readControlResponse (closed)"
876− throwIO $ userError "Invalid request ID"
878− putMVar mvar (Right response)
880 -- Close a connection
856 -- Close a connection
881 -- It is important that we verify that the connection is in fact open,
857 -- It is important that we verify that the connection is in fact open,
882 -- because otherwise we should not decrement the reference count
858 -- because otherwise we should not decrement the reference count
883− closeConnection :: ConnectionId -> IO ()
859+ closeConnection :: LightweightConnectionId -> IO ()
884− closeConnection cid = do
860+ closeConnection lcid = do
885 modifyMVar_ theirState $ \st -> case st of
861 modifyMVar_ theirState $ \st -> case st of
886 RemoteEndPointInvalid _ ->
862 RemoteEndPointInvalid _ ->
887 relyViolation (ourEndPoint, theirEndPoint) "closeConnection (invalid)"
863 relyViolation (ourEndPoint, theirEndPoint) "closeConnection (invalid)"
888 RemoteEndPointInit _ _ _ ->
864 RemoteEndPointInit _ _ _ ->
889 relyViolation (ourEndPoint, theirEndPoint) "closeConnection (init)"
865 relyViolation (ourEndPoint, theirEndPoint) "closeConnection (init)"
890 RemoteEndPointValid vst -> do
866 RemoteEndPointValid vst -> do
891− unless (IntSet.member cid (vst ^. remoteIncoming)) $
867+ unless (Set.member lcid (vst ^. remoteIncoming)) $
892 throwIO $ userError "Invalid CloseConnection"
868 throwIO $ userError "Invalid CloseConnection"
893 return ( RemoteEndPointValid
869 return ( RemoteEndPointValid
894− . (remoteIncoming ^: IntSet.delete cid)
870+ . (remoteIncoming ^: Set.delete lcid)
897 RemoteEndPointClosing _ _ ->
873 RemoteEndPointClosing _ _ ->
@@ -903,13 +879,13 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
903 throwIO err
879 throwIO err
904 RemoteEndPointClosed ->
880 RemoteEndPointClosed ->
905 relyViolation (ourEndPoint, theirEndPoint) "closeConnection (closed)"
881 relyViolation (ourEndPoint, theirEndPoint) "closeConnection (closed)"
906− writeChan ourChannel (ConnectionClosed cid)
882+ writeChan ourChannel (ConnectionClosed (connId lcid))
907− closeIfUnused (ourEndPoint, theirEndPoint)
909 -- Close the socket (if we don't have any outgoing connections)
884 -- Close the socket (if we don't have any outgoing connections)
910− closeSocket :: N.Socket -> IO Bool
885+ closeSocket :: N.Socket -> LightweightConnectionId -> IO Bool
911− closeSocket sock = do
886+ closeSocket sock lastReceivedId = do
912− mAct <- modifyMVar theirState $ \st ->
887+ mAct <- modifyMVar theirState $ \st -> do
888+ lastSentId <- getLastConnOutId ourEndPoint
913 case st of
889 case st of
914 RemoteEndPointInvalid _ ->
890 RemoteEndPointInvalid _ ->
915 relyViolation (ourEndPoint, theirEndPoint)
891 relyViolation (ourEndPoint, theirEndPoint)
@@ -921,25 +897,44 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
921 -- We regard a CloseSocket message as an (optimized) way for the
897 -- We regard a CloseSocket message as an (optimized) way for the
922 -- remote endpoint to indicate that all its connections to us are
898 -- remote endpoint to indicate that all its connections to us are
923 -- now properly closed
899 -- now properly closed
924− forM_ (IntSet.elems $ vst ^. remoteIncoming) $
900+ forM_ (Set.elems $ vst ^. remoteIncoming) $
925− writeChan ourChannel . ConnectionClosed
901+ writeChan ourChannel . ConnectionClosed . connId
926− let vst' = remoteIncoming ^= IntSet.empty $ vst
902+ let vst' = remoteIncoming ^= Set.empty $ vst
927− -- Check if we agree that the connection should be closed
903+ -- If we still have outgoing connections then we ignore the
904+ -- CloseSocket request (we sent a ConnectionCreated message to the
905+ -- remote endpoint, but it did not receive it before sending the
906+ -- CloseSocket request). Similarly, if lastReceivedId < lastSentId
907+ -- then we sent a ConnectionCreated *AND* a ConnectionClosed
908+ -- message to the remote endpoint, *both of which* it did not yet
909+ -- receive before sending the CloseSocket request.
928− if vst' ^. remoteOutgoing == 0
910+ if vst' ^. remoteOutgoing > 0 || lastReceivedId < lastSentId
912+ return (RemoteEndPointValid vst', Nothing)
930 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
914 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
931 -- Attempt to reply (but don't insist)
915 -- Attempt to reply (but don't insist)
932 act <- schedule theirEndPoint $ do
916 act <- schedule theirEndPoint $ do
933− tryIO $ sendOn vst' [encodeInt32 CloseSocket]
917+ tryIO $ sendOn vst' [ encodeInt32 CloseSocket
918+ , encodeInt32 (vst ^. remoteMaxIncoming)
934 tryCloseSocket sock
920 tryCloseSocket sock
935 return (RemoteEndPointClosed, Just act)
921 return (RemoteEndPointClosed, Just act)
937− return (RemoteEndPointValid vst', Nothing)
938− RemoteEndPointClosing resolved _ -> do
922+ RemoteEndPointClosing resolved vst ->
923+ -- Like above, we need to check if there is a ConnectionCreated
924+ -- message that we sent but that the remote endpoint has not yet
925+ -- received. However, since we are in 'closing' state, the only
926+ -- way this may happen is when we sent a ConnectionCreated,
927+ -- ConnectionClosed, and CloseSocket message, none of which have
928+ -- yet been received. We leave the endpoint in closing state in
930+ if lastReceivedId < lastSentId
932+ return (RemoteEndPointClosing resolved vst, Nothing)
939− removeRemoteEndPoint (ourEndPoint, theirEndPoint)
934+ removeRemoteEndPoint (ourEndPoint, theirEndPoint)
940− act <- schedule theirEndPoint $ tryCloseSocket sock
935+ act <- schedule theirEndPoint $ tryCloseSocket sock
941− putMVar resolved ()
936+ putMVar resolved ()
942− return (RemoteEndPointClosed, Just act)
937+ return (RemoteEndPointClosed, Just act)
943 RemoteEndPointFailed err ->
938 RemoteEndPointFailed err ->
944 throwIO err
939 throwIO err
945 RemoteEndPointClosed ->
940 RemoteEndPointClosed ->
@@ -950,13 +945,13 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
950 Just act -> do
945 Just act -> do
951 runScheduledAction (ourEndPoint, theirEndPoint) act
946 runScheduledAction (ourEndPoint, theirEndPoint) act
952 return True
947 return True
954 -- Read a message and output it on the endPoint's channel. By rights we
949 -- Read a message and output it on the endPoint's channel. By rights we
955 -- should verify that the connection ID is valid, but this is unnecessary
950 -- should verify that the connection ID is valid, but this is unnecessary
956 -- overhead
951 -- overhead
957− readMessage :: N.Socket -> ConnectionId -> IO ()
952+ readMessage :: N.Socket -> LightweightConnectionId -> IO ()
958− readMessage sock connId =
953+ readMessage sock lcid =
959− recvWithLength sock >>= writeChan ourChannel . Received connId
954+ recvWithLength sock >>= writeChan ourChannel . Received (connId lcid)
961 -- Arguments
956 -- Arguments
962 ourChannel = localChannel ourEndPoint
957 ourChannel = localChannel ourEndPoint
@@ -975,10 +970,9 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
975 RemoteEndPointInit _ _ _ ->
970 RemoteEndPointInit _ _ _ ->
976 relyViolation (ourEndPoint, theirEndPoint)
971 relyViolation (ourEndPoint, theirEndPoint)
977 "handleIncomingMessages:prematureExit"
972 "handleIncomingMessages:prematureExit"
978− RemoteEndPointValid vst -> do
973+ RemoteEndPointValid _ -> do
979 let code = EventConnectionLost (remoteAddress theirEndPoint)
974 let code = EventConnectionLost (remoteAddress theirEndPoint)
980 writeChan ourChannel . ErrorEvent $ TransportError code (show err)
975 writeChan ourChannel . ErrorEvent $ TransportError code (show err)
981− forM_ (vst ^. pendingCtrlRequests) $ flip putMVar (Left err)
982 return (RemoteEndPointFailed err)
976 return (RemoteEndPointFailed err)
983 RemoteEndPointClosing resolved _ -> do
977 RemoteEndPointClosing resolved _ -> do
984 putMVar resolved ()
978 putMVar resolved ()
@@ -989,6 +983,10 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
989 RemoteEndPointFailed err' ->
983 RemoteEndPointFailed err' ->
990 return (RemoteEndPointFailed err')
984 return (RemoteEndPointFailed err')
986+ -- Construct a connection ID
987+ connId :: LightweightConnectionId -> ConnectionId
988+ connId = createConnectionId (remoteId theirEndPoint)
992--------------------------------------------------------------------------------
990--------------------------------------------------------------------------------
993-- Uninterruptable auxiliary functions --
991-- Uninterruptable auxiliary functions --
@@ -996,19 +994,18 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
996-- be killed.
994-- be killed.
997--------------------------------------------------------------------------------
995--------------------------------------------------------------------------------
999−-- | Request a connection to a remote endpoint
997+-- | Create a connection to a remote endpoint
1001−-- This will block until we get a connection ID from the remote endpoint; if
1002−-- the remote endpoint was in 'RemoteEndPointClosing' state then we will
999+-- If the remote endpoint is in 'RemoteEndPointClosing' state then we will
1003−-- additionally block until that is resolved.
1000+-- block until that is resolved.
1005-- May throw a TransportError ConnectErrorCode exception.
1002-- May throw a TransportError ConnectErrorCode exception.
1006−requestConnectionTo :: TCPParameters
1003+createConnectionTo :: TCPParameters
1007 -> LocalEndPoint
1004 -> LocalEndPoint
1008 -> EndPointAddress
1005 -> EndPointAddress
1009 -> ConnectHints
1006 -> ConnectHints
1010− -> IO (RemoteEndPoint, ConnectionId)
1007+ -> IO (RemoteEndPoint, LightweightConnectionId)
1011−requestConnectionTo params ourEndPoint theirAddress hints = go
1008+createConnectionTo params ourEndPoint theirAddress hints = go
1014 (theirEndPoint, isNew) <- mapIOException connectFailed $
1011 (theirEndPoint, isNew) <- mapIOException connectFailed $
@@ -1020,9 +1017,26 @@ requestConnectionTo params ourEndPoint theirAddress hints = go
1020 setupRemoteEndPoint params (ourEndPoint, theirEndPoint) hints
1017 setupRemoteEndPoint params (ourEndPoint, theirEndPoint) hints
1020+ -- 'findRemoteEndPoint' will have increased 'remoteOutgoing'
1023− reply <- mapIOException connectFailed $
1021+ mapIOException connectFailed $ do
1024− doRemoteRequest (ourEndPoint, theirEndPoint) RequestConnectionId
1022+ act <- withMVar (remoteState theirEndPoint) $ \st -> case st of
1025− return (theirEndPoint, decodeInt32 . BS.concat $ reply)
1023+ RemoteEndPointValid vst -> do
1024+ connId <- getNextConnOutId ourEndPoint
1025+ schedule theirEndPoint $ do
1026+ sendOn vst [encodeInt32 CreatedNewConnection, encodeInt32 connId]
1029+ RemoteEndPointInvalid err ->
1031+ RemoteEndPointFailed err ->
1033+ -- Algorithmic errors
1035+ relyViolation (ourEndPoint, theirEndPoint) "createConnectionTo"
1036+ -- TODO: deal with exception case?
1037+ connId <- runScheduledAction (ourEndPoint, theirEndPoint) act
1038+ return (theirEndPoint, connId)
1027 connectFailed :: IOException -> TransportError ConnectErrorCode
1041 connectFailed :: IOException -> TransportError ConnectErrorCode
1028 connectFailed = TransportError ConnectFailed . show
1042 connectFailed = TransportError ConnectFailed . show
@@ -1042,12 +1056,11 @@ setupRemoteEndPoint params (ourEndPoint, theirEndPoint) hints = do
1042 Right (sock, ConnectionRequestAccepted) -> do
1056 Right (sock, ConnectionRequestAccepted) -> do
1043 sendLock <- newMVar ()
1057 sendLock <- newMVar ()
1044 let vst = ValidRemoteEndPointState
1058 let vst = ValidRemoteEndPointState
1045− { remoteSocket = sock
1059+ { remoteSocket = sock
1046− , remoteSendLock = sendLock
1060+ , remoteSendLock = sendLock
1047− , _remoteOutgoing = 0
1061+ , _remoteOutgoing = 0
1048− , _remoteIncoming = IntSet.empty
1062+ , _remoteIncoming = Set.empty
1049− , _pendingCtrlRequests = IntMap.empty
1063+ , _remoteMaxIncoming = 0
1050− , _nextCtrlRequestId = 0
1052 resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointValid vst)
1065 resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointValid vst)
1053 return True
1066 return True
@@ -1076,56 +1089,25 @@ setupRemoteEndPoint params (ourEndPoint, theirEndPoint) hints = do
1076 theirAddress = remoteAddress theirEndPoint
1089 theirAddress = remoteAddress theirEndPoint
1077 invalidAddress = TransportError ConnectNotFound
1090 invalidAddress = TransportError ConnectNotFound
1079−-- | Do a (blocking) remote request
1081−-- May throw IO (user) exception if the local or the remote endpoint is closed,
1082−-- if the send fails, or if the remote endpoint fails before it replies.
1083−doRemoteRequest :: EndPointPair -> ControlHeader -> IO [ByteString]
1084−doRemoteRequest (ourEndPoint, theirEndPoint) header = do
1085− replyMVar <- newEmptyMVar
1086− act <- modifyMVar (remoteState theirEndPoint) $ \st -> case st of
1087− RemoteEndPointValid vst -> do
1088− let reqId = vst ^. nextCtrlRequestId
1089− act <- schedule theirEndPoint $ sendOn vst [encodeInt32 header, encodeInt32 reqId]
1090− return ( RemoteEndPointValid
1091− . (nextCtrlRequestId ^: (+ 1))
1092− . (pendingCtrlRequestsAt reqId ^= Just replyMVar)
1097− RemoteEndPointInvalid err ->
1099− RemoteEndPointInit _ _ _ ->
1100− relyViolation (ourEndPoint, theirEndPoint) "doRemoteRequest (init)"
1101− RemoteEndPointClosing _ _ ->
1102− relyViolation (ourEndPoint, theirEndPoint) "doRemoteRequest (closing)"
1103− RemoteEndPointClosed ->
1104− relyViolation (ourEndPoint, theirEndPoint) "doRemoteRequest (closed)"
1105− RemoteEndPointFailed err ->
1107− runScheduledAction (ourEndPoint, theirEndPoint) act
1108− mReply <- takeMVar replyMVar
1110− Left err -> throwIO err
1111− Right reply -> return reply
1113-- | Send a CloseSocket request if the remote endpoint is unused
1092-- | Send a CloseSocket request if the remote endpoint is unused
1114closeIfUnused :: EndPointPair -> IO ()
1093closeIfUnused :: EndPointPair -> IO ()
1115closeIfUnused (ourEndPoint, theirEndPoint) = do
1094closeIfUnused (ourEndPoint, theirEndPoint) = do
1116 mAct <- modifyMVar (remoteState theirEndPoint) $ \st -> case st of
1095 mAct <- modifyMVar (remoteState theirEndPoint) $ \st -> case st of
1117 RemoteEndPointValid vst ->
1096 RemoteEndPointValid vst ->
1118− if vst ^. remoteOutgoing == 0 && IntSet.null (vst ^. remoteIncoming)
1097+ if vst ^. remoteOutgoing == 0 && Set.null (vst ^. remoteIncoming)
1119 then do
1098 then do
1120 resolved <- newEmptyMVar
1099 resolved <- newEmptyMVar
1100+ act <- schedule theirEndPoint $
1121− act <- schedule theirEndPoint $ sendOn vst [encodeInt32 CloseSocket]
1101+ sendOn vst [ encodeInt32 CloseSocket
1102+ , encodeInt32 (vst ^. remoteMaxIncoming)
1122 return (RemoteEndPointClosing resolved vst, Just act)
1104 return (RemoteEndPointClosing resolved vst, Just act)
1124 return (RemoteEndPointValid vst, Nothing)
1106 return (RemoteEndPointValid vst, Nothing)
1126 return (st, Nothing)
1108 return (st, Nothing)
1127 forM_ mAct $ runScheduledAction (ourEndPoint, theirEndPoint)
1109 forM_ mAct $ runScheduledAction (ourEndPoint, theirEndPoint)
1129-- | Reset a remote endpoint if it is in Invalid mode
1111-- | Reset a remote endpoint if it is in Invalid mode
1131-- If the remote endpoint is currently in broken state, and
1113-- If the remote endpoint is currently in broken state, and
@@ -1160,7 +1142,8 @@ connectToSelf :: LocalEndPoint
1160 -> IO Connection
1142 -> IO Connection
1161connectToSelf ourEndPoint = do
1143connectToSelf ourEndPoint = do
1162 connAlive <- newIORef True -- Protected by the local endpoint lock
1144 connAlive <- newIORef True -- Protected by the local endpoint lock
1163− connId <- mapIOException connectFailed $ getNextConnectionId ourEndPoint
1145+ lconnId <- mapIOException connectFailed $ getNextConnOutId ourEndPoint
1146+ let connId = createConnectionId heavyweightSelfConnectionId lconnId
1164 writeChan ourChan $
1147 writeChan ourChan $
1165 ConnectionOpened connId ReliableOrdered (localAddress ourEndPoint)
1148 ConnectionOpened connId ReliableOrdered (localAddress ourEndPoint)
1166 return Connection
1149 return Connection
@@ -1214,21 +1197,33 @@ resolveInit (ourEndPoint, theirEndPoint) newState =
1215 relyViolation (ourEndPoint, theirEndPoint) "resolveInit"
1198 relyViolation (ourEndPoint, theirEndPoint) "resolveInit"
1217−-- | Get the next connection ID
1200+-- | Get the next outgoing connection ID
1219-- Throws an IO exception when the endpoint is closed.
1202-- Throws an IO exception when the endpoint is closed.
1220−getNextConnectionId :: LocalEndPoint -> IO ConnectionId
1203+getNextConnOutId :: LocalEndPoint -> IO LightweightConnectionId
1221−getNextConnectionId ourEndpoint =
1204+getNextConnOutId ourEndpoint =
1222 modifyMVar (localState ourEndpoint) $ \st -> case st of
1205 modifyMVar (localState ourEndpoint) $ \st -> case st of
1223 LocalEndPointValid vst -> do
1206 LocalEndPointValid vst -> do
1224− let connId = vst ^. nextConnectionId
1207+ let connId = vst ^. nextConnOutId
1225 return ( LocalEndPointValid
1208 return ( LocalEndPointValid
1226− . (nextConnectionId ^= connId + 1)
1209+ . (nextConnOutId ^= connId + 1)
1228 , connId)
1211 , connId)
1229 LocalEndPointClosed ->
1212 LocalEndPointClosed ->
1230 throwIO $ userError "Local endpoint closed"
1213 throwIO $ userError "Local endpoint closed"
1215+-- | The last outgoing connection ID we created, or zero if we never created any
1216+getLastConnOutId :: LocalEndPoint -> IO LightweightConnectionId
1217+getLastConnOutId ourEndPoint =
1218+ withMVar (localState ourEndPoint) $ \st -> case st of
1219+ LocalEndPointValid vst ->
1220+ let nextId = vst ^. nextConnOutId in
1221+ if nextId == firstNonReservedLightweightConnectionId
1223+ else return (nextId - 1)
1224+ LocalEndPointClosed ->
1225+ throwIO $ userError "Local endpoint closed"
1232-- | Create a new local endpoint
1227-- | Create a new local endpoint
1234-- May throw a TransportError NewEndPointErrorCode exception if the transport
1229-- May throw a TransportError NewEndPointErrorCode exception if the transport
@@ -1237,9 +1232,9 @@ createLocalEndPoint :: TCPTransport -> IO LocalEndPoint
1237createLocalEndPoint transport = do
1232createLocalEndPoint transport = do
1238 chan <- newChan
1233 chan <- newChan
1239 state <- newMVar . LocalEndPointValid $ ValidLocalEndPointState
1234 state <- newMVar . LocalEndPointValid $ ValidLocalEndPointState
1240− { _nextConnectionId = firstNonReservedConnectionId
1235+ { _nextConnOutId = firstNonReservedLightweightConnectionId
1236+ , _nextConnInId = firstNonReservedHeavyweightConnectionId
1241− , _localConnections = Map.empty
1237+ , _localConnections = Map.empty
1242− , _nextRemoteId = 0
1244 modifyMVar (transportState transport) $ \st -> case st of
1239 modifyMVar (transportState transport) $ \st -> case st of
1245 TransportValid vst -> do
1240 TransportValid vst -> do
@@ -1321,12 +1316,12 @@ findRemoteEndPoint ourEndPoint theirAddress findOrigin = go
1321 let theirEndPoint = RemoteEndPoint
1316 let theirEndPoint = RemoteEndPoint
1322 { remoteAddress = theirAddress
1317 { remoteAddress = theirAddress
1323 , remoteState = theirState
1318 , remoteState = theirState
1324− , remoteId = vst ^. nextRemoteId
1319+ , remoteId = vst ^. nextConnInId
1325 , remoteScheduled = scheduled
1320 , remoteScheduled = scheduled
1327 return ( LocalEndPointValid
1322 return ( LocalEndPointValid
1328 . (localConnectionTo theirAddress ^= Just theirEndPoint)
1323 . (localConnectionTo theirAddress ^= Just theirEndPoint)
1329− . (nextRemoteId ^: (+ 1))
1324+ . (nextConnInId ^: (+ 1))
1331 , (theirEndPoint, True)
1326 , (theirEndPoint, True)
@@ -1504,6 +1499,13 @@ decodeEndPointAddress (EndPointAddress bs) =
1502+-- | Construct a ConnectionId
1503+createConnectionId :: HeavyweightConnectionId
1504+ -> LightweightConnectionId
1506+createConnectionId hcid lcid =
1507+ (fromIntegral hcid `shiftL` 32) .|. fromIntegral lcid
1507-- | @spltiMaxFromEnd p n xs@ splits list @xs@ at elements matching @p@,
1509-- | @spltiMaxFromEnd p n xs@ splits list @xs@ at elements matching @p@,
1508-- returning at most @p@ segments -- counting from the /end/
1510-- returning at most @p@ segments -- counting from the /end/
@@ -1564,8 +1566,16 @@ internalSocketBetween transport ourAddress theirAddress = do
1564--------------------------------------------------------------------------------
1566--------------------------------------------------------------------------------
1566-- | We reserve a bunch of connection IDs for control messages
1568-- | We reserve a bunch of connection IDs for control messages
1567−firstNonReservedConnectionId :: ConnectionId
1569+firstNonReservedLightweightConnectionId :: LightweightConnectionId
1568−firstNonReservedConnectionId = 1024
1570+firstNonReservedLightweightConnectionId = 1024
1572+-- | Self-connection
1573+heavyweightSelfConnectionId :: HeavyweightConnectionId
1574+heavyweightSelfConnectionId = 0
1576+-- | We reserve some connection IDs for special heavyweight connections
1577+firstNonReservedHeavyweightConnectionId :: HeavyweightConnectionId
1578+firstNonReservedHeavyweightConnectionId = 1
1570--------------------------------------------------------------------------------
1580--------------------------------------------------------------------------------
1571-- Accessor definitions --
1581-- Accessor definitions --
@@ -1577,35 +1587,28 @@ localEndPoints = accessor _localEndPoints (\es st -> st { _localEndPoints = es }
1577nextEndPointId :: Accessor ValidTransportState EndPointId
1587nextEndPointId :: Accessor ValidTransportState EndPointId
1578nextEndPointId = accessor _nextEndPointId (\eid st -> st { _nextEndPointId = eid })
1588nextEndPointId = accessor _nextEndPointId (\eid st -> st { _nextEndPointId = eid })
1580−nextConnectionId :: Accessor ValidLocalEndPointState ConnectionId
1590+nextConnOutId :: Accessor ValidLocalEndPointState LightweightConnectionId
1581−nextConnectionId = accessor _nextConnectionId (\cix st -> st { _nextConnectionId = cix })
1591+nextConnOutId = accessor _nextConnOutId (\cix st -> st { _nextConnOutId = cix })
1583localConnections :: Accessor ValidLocalEndPointState (Map EndPointAddress RemoteEndPoint)
1593localConnections :: Accessor ValidLocalEndPointState (Map EndPointAddress RemoteEndPoint)
1584localConnections = accessor _localConnections (\es st -> st { _localConnections = es })
1594localConnections = accessor _localConnections (\es st -> st { _localConnections = es })
1586−nextRemoteId :: Accessor ValidLocalEndPointState Int
1596+nextConnInId :: Accessor ValidLocalEndPointState HeavyweightConnectionId
1587−nextRemoteId = accessor _nextRemoteId (\rid st -> st { _nextRemoteId = rid })
1597+nextConnInId = accessor _nextConnInId (\rid st -> st { _nextConnInId = rid })
1589remoteOutgoing :: Accessor ValidRemoteEndPointState Int
1599remoteOutgoing :: Accessor ValidRemoteEndPointState Int
1590remoteOutgoing = accessor _remoteOutgoing (\cs conn -> conn { _remoteOutgoing = cs })
1600remoteOutgoing = accessor _remoteOutgoing (\cs conn -> conn { _remoteOutgoing = cs })
1592−remoteIncoming :: Accessor ValidRemoteEndPointState IntSet
1602+remoteIncoming :: Accessor ValidRemoteEndPointState (Set LightweightConnectionId)
1593remoteIncoming = accessor _remoteIncoming (\cs conn -> conn { _remoteIncoming = cs })
1603remoteIncoming = accessor _remoteIncoming (\cs conn -> conn { _remoteIncoming = cs })
1595−pendingCtrlRequests :: Accessor ValidRemoteEndPointState (IntMap (MVar (Either IOException [ByteString])))
1596−pendingCtrlRequests = accessor _pendingCtrlRequests (\rep st -> st { _pendingCtrlRequests = rep })
1598−nextCtrlRequestId :: Accessor ValidRemoteEndPointState ControlRequestId
1605+remoteMaxIncoming :: Accessor ValidRemoteEndPointState LightweightConnectionId
1599−nextCtrlRequestId = accessor _nextCtrlRequestId (\cid st -> st { _nextCtrlRequestId = cid })
1606+remoteMaxIncoming = accessor _remoteMaxIncoming (\lcid st -> st { _remoteMaxIncoming = lcid })
1601localEndPointAt :: EndPointAddress -> Accessor ValidTransportState (Maybe LocalEndPoint)
1608localEndPointAt :: EndPointAddress -> Accessor ValidTransportState (Maybe LocalEndPoint)
1602localEndPointAt addr = localEndPoints >>> DAC.mapMaybe addr
1609localEndPointAt addr = localEndPoints >>> DAC.mapMaybe addr
1604−pendingCtrlRequestsAt :: ControlRequestId -> Accessor ValidRemoteEndPointState (Maybe (MVar (Either IOException [ByteString])))
1605−pendingCtrlRequestsAt ix = pendingCtrlRequests >>> DAC.intMapMaybe (fromIntegral ix)
1607−localConnectionTo :: EndPointAddress
1608− -> Accessor ValidLocalEndPointState (Maybe RemoteEndPoint)
1611+localConnectionTo :: EndPointAddress -> Accessor ValidLocalEndPointState (Maybe RemoteEndPoint)
1609localConnectionTo addr = localConnections >>> DAC.mapMaybe addr
1612localConnectionTo addr = localConnections >>> DAC.mapMaybe addr
1611-------------------------------------------------------------------------------
1614-------------------------------------------------------------------------------