@@ -23,8 +23,10 @@ module Network.Transport.TCP
2323 , decodeEndPointAddress
2424 , ControlHeader(..)
2525 , ConnectionRequestResponse(..)
26− , firstNonReservedConnectionId
26+ , firstNonReservedLightweightConnectionId
27+ , firstNonReservedHeavyweightConnectionId
2728 , socketToEndPoint
29+ , LightweightConnectionId
2830 -- * Design notes
2931 -- $design
3032 ) where
@@ -45,7 +47,6 @@ import Network.Transport.TCP.Internal
4547 )
4648import Network.Transport.Internal
4749 ( encodeInt32
48− , decodeInt32
4950 , prependLength
5051 , mapIOException
5152 , tryIO
@@ -79,7 +80,6 @@ import Control.Concurrent.MVar
7980 , modifyMVar
8081 , modifyMVar_
8182 , readMVar
82− , takeMVar
8383 , putMVar
8484 , newEmptyMVar
8585 , withMVar
@@ -103,11 +103,10 @@ import Data.IORef (IORef, newIORef, writeIORef, readIORef)
103103import Data.ByteString (ByteString)
104104import qualified Data.ByteString as BS (concat)
105105import qualified Data.ByteString.Char8 as BSC (pack, unpack)
106−import Data.Int (Int32)
107−import Data.IntMap (IntMap)
108−import qualified Data.IntMap as IntMap (empty)
109−import Data.IntSet (IntSet)
110−import qualified Data.IntSet as IntSet
106+import Data.Bits (shiftL, (.|.))
107+import Data.Word (Word32)
108+import Data.Set (Set)
109+import qualified Data.Set as Set
111110 ( empty
112111 , insert
113112 , elems
@@ -119,7 +118,7 @@ import qualified Data.IntSet as IntSet
119118import Data.Map (Map)
120119import qualified Data.Map as Map (empty)
121120import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
122−import qualified Data.Accessor.Container as DAC (mapMaybe, intMapMaybe)
121+import qualified Data.Accessor.Container as DAC (mapMaybe)
123122import Data.Foldable (forM_, mapM_)
124123
125124-- $design
@@ -256,9 +255,9 @@ data LocalEndPointState =
256255 | LocalEndPointClosed
257256
258257data ValidLocalEndPointState = ValidLocalEndPointState
259− { _nextConnectionId :: !ConnectionId
258+ { _nextConnOutId :: !LightweightConnectionId
259+ , _nextConnInId :: !HeavyweightConnectionId
260260 , _localConnections :: !(Map EndPointAddress RemoteEndPoint)
261− , _nextRemoteId :: !Int
262261 }
263262
264263-- REMOTE ENDPOINTS
@@ -278,14 +277,14 @@ data ValidLocalEndPointState = ValidLocalEndPointState
278277-- \-------+----------+--------> Failed
279278--
280279-- 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
282281-- handleConnectionRequest (when a remote node tries to connect to us).
283282-- 'Init' carries an MVar () 'resolved' which concurrent threads can use to
284283-- wait for the remote endpoint to finish initialization. We record who
285284-- requested the connection (the local endpoint or the remote endpoint).
286285--
287286-- 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.
289288--
290289-- Valid: This is the "normal" state for a working remote endpoint.
291290--
@@ -346,7 +345,7 @@ data ValidLocalEndPointState = ValidLocalEndPointState
346345data RemoteEndPoint = RemoteEndPoint
347346 { remoteAddress :: !EndPointAddress
348347 , remoteState :: !(MVar RemoteState)
349− , remoteId :: !Int
348+ , remoteId :: !HeavyweightConnectionId
350349 , remoteScheduled :: !(Chan (IO ()))
351350 }
352351
@@ -368,35 +367,43 @@ data RemoteState =
368367 -- using a closeTransport or closeEndPoint API call
369368 | RemoteEndPointFailed !IOException
370369
370+-- TODO: we might want to replace Set (here and elsewhere) by faster
371+-- containers
372+--
373+-- TODO: we could get rid of 'remoteIncoming' (and maintain less state) if
374+-- we introduce a new event 'AllConnectionsClosed'
371375data ValidRemoteEndPointState = ValidRemoteEndPointState
372376 { _remoteOutgoing :: !Int
373− , _remoteIncoming :: !IntSet
377+ , _remoteIncoming :: !(Set LightweightConnectionId)
378+ , _remoteMaxIncoming :: !LightweightConnectionId
374379 , remoteSocket :: !N.Socket
375380 , remoteSendLock :: !(MVar ())
376− , _pendingCtrlRequests :: !(IntMap (MVar (Either IOException [ByteString])))
377− , _nextCtrlRequestId :: !ControlRequestId
378381 }
379382
380383-- | Local identifier for an endpoint within this transport
381−type EndPointId = Int32
382−
383−-- | Control request ID
384−--
385−-- Control requests are asynchronous; the request ID makes it possible to match
386−-- requests and replies
387−type ControlRequestId = Int32
384+type EndPointId = Word32
388385
389386-- | Pair of local and a remote endpoint (for conciseness in signatures)
390−type EndPointPair = (LocalEndPoint, RemoteEndPoint)
387+type EndPointPair = (LocalEndPoint, RemoteEndPoint)
388+
389+-- | Lightweight connection ID (sender allocated)
390+--
391+-- A ConnectionId is the concentation of a 'HeavyweightConnectionId' and a
392+-- 'LightweightConnectionId'.
393+type LightweightConnectionId = Word32
394+
395+-- | Heavyweight connection ID (recipient allocated)
396+--
397+-- A ConnectionId is the concentation of a 'HeavyweightConnectionId' and a
398+-- 'LightweightConnectionId'.
399+type HeavyweightConnectionId = Word32
391400
392401-- | Control headers
393402data ControlHeader =
394− -- | Request a new connection ID from the remote endpoint
395− RequestConnectionId
403+ -- | Tell the remote endpoint that we created a new connection
404+ CreatedNewConnection
396405 -- | Tell the remote endpoint we will no longer be using a connection
397406 | CloseConnection
398− -- | Respond to a control request /from/ the remote endpoint
399− | ControlResponse
400407 -- | Request to close the connection (see module description)
401408 | CloseSocket
402409 deriving (Enum, Bounded, Show)
@@ -558,7 +565,7 @@ apiConnect params ourEndPoint theirAddress _reliability hints =
558565 else do
559566 resetIfBroken ourEndPoint theirAddress
560567 (theirEndPoint, connId) <-
561− requestConnectionTo params ourEndPoint theirAddress hints
568+ createConnectionTo params ourEndPoint theirAddress hints
562569 -- connAlive can be an IORef rather than an MVar because it is protected
563570 -- by the remoteState MVar. We don't need the overhead of locking twice.
564571 connAlive <- newIORef True
@@ -568,7 +575,7 @@ apiConnect params ourEndPoint theirAddress _reliability hints =
568575 }
569576
570577-- | Close a connection
571−apiClose :: EndPointPair -> ConnectionId -> IORef Bool -> IO ()
578+apiClose :: EndPointPair -> LightweightConnectionId -> IORef Bool -> IO ()
572579apiClose (ourEndPoint, theirEndPoint) connId connAlive =
573580 void . tryIO . asyncWhenCancelled return $ do
574581 mAct <- modifyMVar (remoteState theirEndPoint) $ \st -> case st of
@@ -593,10 +600,10 @@ apiClose (ourEndPoint, theirEndPoint) connId connAlive =
593600
594601
595602-- | Send data across a connection
596−apiSend :: EndPointPair -- ^ Local and remote endpoint
597− -> ConnectionId -- ^ Connection ID (supplied by remote endpoint)
598− -> IORef Bool -- ^ Is the connection still alive?
599− -> [ByteString] -- ^ Payload
603+apiSend :: EndPointPair -- ^ Local and remote endpoint
604+ -> LightweightConnectionId -- ^ Connection ID
605+ -> IORef Bool -- ^ Is the connection still alive?
606+ -> [ByteString] -- ^ Payload
600607 -> IO (Either (TransportError SendErrorCode) ())
601608apiSend (ourEndPoint, theirEndPoint) connId connAlive payload =
602609 -- We don't need the overhead of asyncWhenCancelled here
@@ -665,7 +672,9 @@ apiCloseEndPoint transport evs ourEndPoint =
665672 return (closed, Nothing)
666673 RemoteEndPointValid vst -> do
667674 act <- schedule theirEndPoint $ do
668− tryIO $ sendOn vst [encodeInt32 CloseSocket]
675+ tryIO $ sendOn vst [ encodeInt32 CloseSocket
676+ , encodeInt32 (vst ^. remoteMaxIncoming)
677+ ]
669678 tryCloseSocket (remoteSocket vst)
670679 return (closed, Just act)
671680 RemoteEndPointClosing resolved vst -> do
@@ -729,12 +738,11 @@ handleConnectionRequest transport sock = handle handleException $ do
729738 else do
730739 sendLock <- newMVar ()
731740 let vst = ValidRemoteEndPointState
732− { remoteSocket = sock
733− , remoteSendLock = sendLock
734− , _remoteOutgoing = 0
735− , _remoteIncoming = IntSet.empty
736− , _pendingCtrlRequests = IntMap.empty
737− , _nextCtrlRequestId = 0
741+ { remoteSocket = sock
742+ , remoteSendLock = sendLock
743+ , _remoteOutgoing = 0
744+ , _remoteIncoming = Set.empty
745+ , _remoteMaxIncoming = 0
738746 }
739747 sendMany sock [encodeInt32 ConnectionRequestAccepted]
740748 resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointValid vst)
@@ -790,32 +798,28 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
790798 -- exception thrown by 'recv'.
791799 go :: N.Socket -> IO ()
792800 go sock = do
793− connId <- recvInt32 sock
794− if connId >= firstNonReservedConnectionId
801+ lcid <- recvInt32 sock :: IO LightweightConnectionId
802+ if lcid >= firstNonReservedLightweightConnectionId
795803 then do
796− readMessage sock connId
804+ readMessage sock lcid
797805 go sock
798806 else
799− case tryToEnum (fromIntegral connId) of
800− Just RequestConnectionId -> do
801− recvInt32 sock >>= createNewConnection
807+ case tryToEnum (fromIntegral lcid) of
808+ Just CreatedNewConnection -> do
809+ recvInt32 sock >>= createdNewConnection
802810 go sock
803− Just ControlResponse -> do
804− recvInt32 sock >>= readControlResponse sock
805− go sock
806811 Just CloseConnection -> do
807812 recvInt32 sock >>= closeConnection
808813 go sock
809814 Just CloseSocket -> do
810− didClose <- closeSocket sock
815+ didClose <- recvInt32 sock >>= closeSocket sock
811816 unless didClose $ go sock
812817 Nothing ->
813818 throwIO $ userError "Invalid control request"
814819
815820 -- Create a new connection
816− createNewConnection :: ControlRequestId -> IO ()
817− createNewConnection reqId = do
818− newId <- getNextConnectionId ourEndPoint
821+ createdNewConnection :: LightweightConnectionId -> IO ()
822+ createdNewConnection lcid = do
819823 modifyMVar_ theirState $ \st -> do
820824 vst <- case st of
821825 RemoteEndPointInvalid _ ->
@@ -825,73 +829,45 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
825829 relyViolation (ourEndPoint, theirEndPoint)
826830 "handleIncomingMessages:createNewConnection (init)"
827831 RemoteEndPointValid vst ->
828− return (remoteIncoming ^: IntSet.insert newId $ vst)
832+ return ( (remoteIncoming ^: Set.insert lcid)
833+ $ (remoteMaxIncoming ^= lcid)
834+ vst
835+ )
829836 RemoteEndPointClosing resolved vst -> do
830837 -- If the endpoint is in closing state that means we send a
831838 -- CloseSocket request to the remote endpoint. If the remote
832− -- endpoint replies with the request to create a new connection, it
833− -- either ignored our request or it sent the request before it got
834− -- ours. Either way, at this point we simply restore the endpoint
835− -- to RemoteEndPointValid
839+ -- endpoint replies that it created a new connection, it either
840+ -- ignored our request or it sent the request before it got ours.
841+ -- Either way, at this point we simply restore the endpoint to
842+ -- RemoteEndPointValid
836843 putMVar resolved ()
837− return (remoteIncoming ^= IntSet.singleton newId $ vst)
844+ return ( (remoteIncoming ^= Set.singleton lcid)
845+ . (remoteMaxIncoming ^= lcid)
846+ $ vst
847+ )
838848 RemoteEndPointFailed err ->
839849 throwIO err
840850 RemoteEndPointClosed ->
841851 relyViolation (ourEndPoint, theirEndPoint)
842852 "createNewConnection (closed)"
843− sendOn vst ( encodeInt32 ControlResponse
844− : encodeInt32 reqId
845− : prependLength [encodeInt32 newId]
846− )
847853 return (RemoteEndPointValid vst)
848− writeChan ourChannel (ConnectionOpened newId ReliableOrdered theirAddr)
849−
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)
864− $ vst
865− , vst ^. pendingCtrlRequestsAt reqId
866− )
867− RemoteEndPointClosing _ _ ->
868− throwIO $ userError "Invalid control response"
869− RemoteEndPointFailed err ->
870− throwIO err
871− RemoteEndPointClosed ->
872− relyViolation (ourEndPoint, theirEndPoint)
873− "readControlResponse (closed)"
874− case mmvar of
875− Nothing ->
876− throwIO $ userError "Invalid request ID"
877− Just mvar ->
878− putMVar mvar (Right response)
854+ writeChan ourChannel (ConnectionOpened (connId lcid) ReliableOrdered theirAddr)
879855
880856 -- Close a connection
881857 -- It is important that we verify that the connection is in fact open,
882858 -- because otherwise we should not decrement the reference count
883− closeConnection :: ConnectionId -> IO ()
884− closeConnection cid = do
859+ closeConnection :: LightweightConnectionId -> IO ()
860+ closeConnection lcid = do
885861 modifyMVar_ theirState $ \st -> case st of
886862 RemoteEndPointInvalid _ ->
887863 relyViolation (ourEndPoint, theirEndPoint) "closeConnection (invalid)"
888864 RemoteEndPointInit _ _ _ ->
889865 relyViolation (ourEndPoint, theirEndPoint) "closeConnection (init)"
890866 RemoteEndPointValid vst -> do
891− unless (IntSet.member cid (vst ^. remoteIncoming)) $
867+ unless (Set.member lcid (vst ^. remoteIncoming)) $
892868 throwIO $ userError "Invalid CloseConnection"
893869 return ( RemoteEndPointValid
894− . (remoteIncoming ^: IntSet.delete cid)
870+ . (remoteIncoming ^: Set.delete lcid)
895871 $ vst
896872 )
897873 RemoteEndPointClosing _ _ ->
@@ -903,13 +879,13 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
903879 throwIO err
904880 RemoteEndPointClosed ->
905881 relyViolation (ourEndPoint, theirEndPoint) "closeConnection (closed)"
906− writeChan ourChannel (ConnectionClosed cid)
907− closeIfUnused (ourEndPoint, theirEndPoint)
882+ writeChan ourChannel (ConnectionClosed (connId lcid))
908883
909884 -- Close the socket (if we don't have any outgoing connections)
910− closeSocket :: N.Socket -> IO Bool
911− closeSocket sock = do
912− mAct <- modifyMVar theirState $ \st ->
885+ closeSocket :: N.Socket -> LightweightConnectionId -> IO Bool
886+ closeSocket sock lastReceivedId = do
887+ mAct <- modifyMVar theirState $ \st -> do
888+ lastSentId <- getLastConnOutId ourEndPoint
913889 case st of
914890 RemoteEndPointInvalid _ ->
915891 relyViolation (ourEndPoint, theirEndPoint)
@@ -921,25 +897,44 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
921897 -- We regard a CloseSocket message as an (optimized) way for the
922898 -- remote endpoint to indicate that all its connections to us are
923899 -- now properly closed
924− forM_ (IntSet.elems $ vst ^. remoteIncoming) $
925− writeChan ourChannel . ConnectionClosed
926− let vst' = remoteIncoming ^= IntSet.empty $ vst
927− -- Check if we agree that the connection should be closed
928− if vst' ^. remoteOutgoing == 0
929− then do
900+ forM_ (Set.elems $ vst ^. remoteIncoming) $
901+ writeChan ourChannel . ConnectionClosed . connId
902+ let vst' = remoteIncoming ^= Set.empty $ vst
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.
910+ if vst' ^. remoteOutgoing > 0 || lastReceivedId < lastSentId
911+ then
912+ return (RemoteEndPointValid vst', Nothing)
913+ else do
930914 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
931915 -- Attempt to reply (but don't insist)
932916 act <- schedule theirEndPoint $ do
933− tryIO $ sendOn vst' [encodeInt32 CloseSocket]
917+ tryIO $ sendOn vst' [ encodeInt32 CloseSocket
918+ , encodeInt32 (vst ^. remoteMaxIncoming)
919+ ]
934920 tryCloseSocket sock
935921 return (RemoteEndPointClosed, Just act)
936− else
937− return (RemoteEndPointValid vst', Nothing)
938− RemoteEndPointClosing resolved _ -> do
939− removeRemoteEndPoint (ourEndPoint, theirEndPoint)
940− act <- schedule theirEndPoint $ tryCloseSocket sock
941− putMVar resolved ()
942− return (RemoteEndPointClosed, Just act)
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
929+ -- that case.
930+ if lastReceivedId < lastSentId
931+ then
932+ return (RemoteEndPointClosing resolved vst, Nothing)
933+ else do
934+ removeRemoteEndPoint (ourEndPoint, theirEndPoint)
935+ act <- schedule theirEndPoint $ tryCloseSocket sock
936+ putMVar resolved ()
937+ return (RemoteEndPointClosed, Just act)
943938 RemoteEndPointFailed err ->
944939 throwIO err
945940 RemoteEndPointClosed ->
@@ -950,13 +945,13 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
950945 Just act -> do
951946 runScheduledAction (ourEndPoint, theirEndPoint) act
952947 return True
953−
948+
954949 -- Read a message and output it on the endPoint's channel. By rights we
955950 -- should verify that the connection ID is valid, but this is unnecessary
956951 -- overhead
957− readMessage :: N.Socket -> ConnectionId -> IO ()
958− readMessage sock connId =
959− recvWithLength sock >>= writeChan ourChannel . Received connId
952+ readMessage :: N.Socket -> LightweightConnectionId -> IO ()
953+ readMessage sock lcid =
954+ recvWithLength sock >>= writeChan ourChannel . Received (connId lcid)
960955
961956 -- Arguments
962957 ourChannel = localChannel ourEndPoint
@@ -975,10 +970,9 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
975970 RemoteEndPointInit _ _ _ ->
976971 relyViolation (ourEndPoint, theirEndPoint)
977972 "handleIncomingMessages:prematureExit"
978− RemoteEndPointValid vst -> do
973+ RemoteEndPointValid _ -> do
979974 let code = EventConnectionLost (remoteAddress theirEndPoint)
980975 writeChan ourChannel . ErrorEvent $ TransportError code (show err)
981− forM_ (vst ^. pendingCtrlRequests) $ flip putMVar (Left err)
982976 return (RemoteEndPointFailed err)
983977 RemoteEndPointClosing resolved _ -> do
984978 putMVar resolved ()
@@ -989,6 +983,10 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
989983 RemoteEndPointFailed err' ->
990984 return (RemoteEndPointFailed err')
991985
986+ -- Construct a connection ID
987+ connId :: LightweightConnectionId -> ConnectionId
988+ connId = createConnectionId (remoteId theirEndPoint)
989+
992990--------------------------------------------------------------------------------
993991-- Uninterruptable auxiliary functions --
994992-- --
@@ -996,19 +994,18 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
996994-- be killed.
997995--------------------------------------------------------------------------------
998996
999−-- | Request a connection to a remote endpoint
997+-- | Create a connection to a remote endpoint
1000998--
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
1003−-- additionally block until that is resolved.
999+-- If the remote endpoint is in 'RemoteEndPointClosing' state then we will
1000+-- block until that is resolved.
10041001--
10051002-- May throw a TransportError ConnectErrorCode exception.
1006−requestConnectionTo :: TCPParameters
1003+createConnectionTo :: TCPParameters
10071004 -> LocalEndPoint
10081005 -> EndPointAddress
10091006 -> ConnectHints
1010− -> IO (RemoteEndPoint, ConnectionId)
1011−requestConnectionTo params ourEndPoint theirAddress hints = go
1007+ -> IO (RemoteEndPoint, LightweightConnectionId)
1008+createConnectionTo params ourEndPoint theirAddress hints = go
10121009 where
10131010 go = do
10141011 (theirEndPoint, isNew) <- mapIOException connectFailed $
@@ -1020,9 +1017,26 @@ requestConnectionTo params ourEndPoint theirAddress hints = go
10201017 setupRemoteEndPoint params (ourEndPoint, theirEndPoint) hints
10211018 go
10221019 else do
1023− reply <- mapIOException connectFailed $
1024− doRemoteRequest (ourEndPoint, theirEndPoint) RequestConnectionId
1025− return (theirEndPoint, decodeInt32 . BS.concat $ reply)
1020+ -- 'findRemoteEndPoint' will have increased 'remoteOutgoing'
1021+ mapIOException connectFailed $ do
1022+ act <- withMVar (remoteState theirEndPoint) $ \st -> case st of
1023+ RemoteEndPointValid vst -> do
1024+ connId <- getNextConnOutId ourEndPoint
1025+ schedule theirEndPoint $ do
1026+ sendOn vst [encodeInt32 CreatedNewConnection, encodeInt32 connId]
1027+ return connId
1028+ -- Error cases
1029+ RemoteEndPointInvalid err ->
1030+ throwIO err
1031+ RemoteEndPointFailed err ->
1032+ throwIO err
1033+ -- Algorithmic errors
1034+ _ ->
1035+ relyViolation (ourEndPoint, theirEndPoint) "createConnectionTo"
1036+ -- TODO: deal with exception case?
1037+ connId <- runScheduledAction (ourEndPoint, theirEndPoint) act
1038+ return (theirEndPoint, connId)
1039+
10261040
10271041 connectFailed :: IOException -> TransportError ConnectErrorCode
10281042 connectFailed = TransportError ConnectFailed . show
@@ -1042,12 +1056,11 @@ setupRemoteEndPoint params (ourEndPoint, theirEndPoint) hints = do
10421056 Right (sock, ConnectionRequestAccepted) -> do
10431057 sendLock <- newMVar ()
10441058 let vst = ValidRemoteEndPointState
1045− { remoteSocket = sock
1046− , remoteSendLock = sendLock
1047− , _remoteOutgoing = 0
1048− , _remoteIncoming = IntSet.empty
1049− , _pendingCtrlRequests = IntMap.empty
1050− , _nextCtrlRequestId = 0
1059+ { remoteSocket = sock
1060+ , remoteSendLock = sendLock
1061+ , _remoteOutgoing = 0
1062+ , _remoteIncoming = Set.empty
1063+ , _remoteMaxIncoming = 0
10511064 }
10521065 resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointValid vst)
10531066 return True
@@ -1076,56 +1089,25 @@ setupRemoteEndPoint params (ourEndPoint, theirEndPoint) hints = do
10761089 theirAddress = remoteAddress theirEndPoint
10771090 invalidAddress = TransportError ConnectNotFound
10781091
1079−-- | Do a (blocking) remote request
1080−--
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)
1093− $ vst
1094− , act
1095− )
1096− -- Error cases
1097− RemoteEndPointInvalid err ->
1098− throwIO 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 ->
1106− throwIO err
1107− runScheduledAction (ourEndPoint, theirEndPoint) act
1108− mReply <- takeMVar replyMVar
1109− case mReply of
1110− Left err -> throwIO err
1111− Right reply -> return reply
1112−
11131092-- | Send a CloseSocket request if the remote endpoint is unused
11141093closeIfUnused :: EndPointPair -> IO ()
11151094closeIfUnused (ourEndPoint, theirEndPoint) = do
11161095 mAct <- modifyMVar (remoteState theirEndPoint) $ \st -> case st of
11171096 RemoteEndPointValid vst ->
1118− if vst ^. remoteOutgoing == 0 && IntSet.null (vst ^. remoteIncoming)
1097+ if vst ^. remoteOutgoing == 0 && Set.null (vst ^. remoteIncoming)
11191098 then do
11201099 resolved <- newEmptyMVar
1121− act <- schedule theirEndPoint $ sendOn vst [encodeInt32 CloseSocket]
1100+ act <- schedule theirEndPoint $
1101+ sendOn vst [ encodeInt32 CloseSocket
1102+ , encodeInt32 (vst ^. remoteMaxIncoming)
1103+ ]
11221104 return (RemoteEndPointClosing resolved vst, Just act)
11231105 else
11241106 return (RemoteEndPointValid vst, Nothing)
11251107 _ ->
11261108 return (st, Nothing)
11271109 forM_ mAct $ runScheduledAction (ourEndPoint, theirEndPoint)
1128−
1110+
11291111-- | Reset a remote endpoint if it is in Invalid mode
11301112--
11311113-- If the remote endpoint is currently in broken state, and
@@ -1160,7 +1142,8 @@ connectToSelf :: LocalEndPoint
11601142 -> IO Connection
11611143connectToSelf ourEndPoint = do
11621144 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
11641147 writeChan ourChan $
11651148 ConnectionOpened connId ReliableOrdered (localAddress ourEndPoint)
11661149 return Connection
@@ -1214,21 +1197,33 @@ resolveInit (ourEndPoint, theirEndPoint) newState =
12141197 _ ->
12151198 relyViolation (ourEndPoint, theirEndPoint) "resolveInit"
12161199
1217−-- | Get the next connection ID
1200+-- | Get the next outgoing connection ID
12181201--
12191202-- Throws an IO exception when the endpoint is closed.
1220−getNextConnectionId :: LocalEndPoint -> IO ConnectionId
1221−getNextConnectionId ourEndpoint =
1203+getNextConnOutId :: LocalEndPoint -> IO LightweightConnectionId
1204+getNextConnOutId ourEndpoint =
12221205 modifyMVar (localState ourEndpoint) $ \st -> case st of
12231206 LocalEndPointValid vst -> do
1224− let connId = vst ^. nextConnectionId
1207+ let connId = vst ^. nextConnOutId
12251208 return ( LocalEndPointValid
1226− . (nextConnectionId ^= connId + 1)
1209+ . (nextConnOutId ^= connId + 1)
12271210 $ vst
12281211 , connId)
12291212 LocalEndPointClosed ->
12301213 throwIO $ userError "Local endpoint closed"
12311214
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
1222+ then return 0
1223+ else return (nextId - 1)
1224+ LocalEndPointClosed ->
1225+ throwIO $ userError "Local endpoint closed"
1226+
12321227-- | Create a new local endpoint
12331228--
12341229-- May throw a TransportError NewEndPointErrorCode exception if the transport
@@ -1237,9 +1232,9 @@ createLocalEndPoint :: TCPTransport -> IO LocalEndPoint
12371232createLocalEndPoint transport = do
12381233 chan <- newChan
12391234 state <- newMVar . LocalEndPointValid $ ValidLocalEndPointState
1240− { _nextConnectionId = firstNonReservedConnectionId
1241− , _localConnections = Map.empty
1242− , _nextRemoteId = 0
1235+ { _nextConnOutId = firstNonReservedLightweightConnectionId
1236+ , _nextConnInId = firstNonReservedHeavyweightConnectionId
1237+ , _localConnections = Map.empty
12431238 }
12441239 modifyMVar (transportState transport) $ \st -> case st of
12451240 TransportValid vst -> do
@@ -1321,12 +1316,12 @@ findRemoteEndPoint ourEndPoint theirAddress findOrigin = go
13211316 let theirEndPoint = RemoteEndPoint
13221317 { remoteAddress = theirAddress
13231318 , remoteState = theirState
1324− , remoteId = vst ^. nextRemoteId
1319+ , remoteId = vst ^. nextConnInId
13251320 , remoteScheduled = scheduled
13261321 }
13271322 return ( LocalEndPointValid
13281323 . (localConnectionTo theirAddress ^= Just theirEndPoint)
1329− . (nextRemoteId ^: (+ 1))
1324+ . (nextConnInId ^: (+ 1))
13301325 $ vst
13311326 , (theirEndPoint, True)
13321327 )
@@ -1504,6 +1499,13 @@ decodeEndPointAddress (EndPointAddress bs) =
15041499 _ ->
15051500 Nothing
15061501
1502+-- | Construct a ConnectionId
1503+createConnectionId :: HeavyweightConnectionId
1504+ -> LightweightConnectionId
1505+ -> ConnectionId
1506+createConnectionId hcid lcid =
1507+ (fromIntegral hcid `shiftL` 32) .|. fromIntegral lcid
1508+
15071509-- | @spltiMaxFromEnd p n xs@ splits list @xs@ at elements matching @p@,
15081510-- returning at most @p@ segments -- counting from the /end/
15091511--
@@ -1564,8 +1566,16 @@ internalSocketBetween transport ourAddress theirAddress = do
15641566--------------------------------------------------------------------------------
15651567
15661568-- | We reserve a bunch of connection IDs for control messages
1567−firstNonReservedConnectionId :: ConnectionId
1568−firstNonReservedConnectionId = 1024
1569+firstNonReservedLightweightConnectionId :: LightweightConnectionId
1570+firstNonReservedLightweightConnectionId = 1024
1571+
1572+-- | Self-connection
1573+heavyweightSelfConnectionId :: HeavyweightConnectionId
1574+heavyweightSelfConnectionId = 0
1575+
1576+-- | We reserve some connection IDs for special heavyweight connections
1577+firstNonReservedHeavyweightConnectionId :: HeavyweightConnectionId
1578+firstNonReservedHeavyweightConnectionId = 1
15691579
15701580--------------------------------------------------------------------------------
15711581-- Accessor definitions --
@@ -1577,35 +1587,28 @@ localEndPoints = accessor _localEndPoints (\es st -> st { _localEndPoints = es }
15771587nextEndPointId :: Accessor ValidTransportState EndPointId
15781588nextEndPointId = accessor _nextEndPointId (\eid st -> st { _nextEndPointId = eid })
15791589
1580−nextConnectionId :: Accessor ValidLocalEndPointState ConnectionId
1581−nextConnectionId = accessor _nextConnectionId (\cix st -> st { _nextConnectionId = cix })
1590+nextConnOutId :: Accessor ValidLocalEndPointState LightweightConnectionId
1591+nextConnOutId = accessor _nextConnOutId (\cix st -> st { _nextConnOutId = cix })
15821592
15831593localConnections :: Accessor ValidLocalEndPointState (Map EndPointAddress RemoteEndPoint)
15841594localConnections = accessor _localConnections (\es st -> st { _localConnections = es })
15851595
1586−nextRemoteId :: Accessor ValidLocalEndPointState Int
1587−nextRemoteId = accessor _nextRemoteId (\rid st -> st { _nextRemoteId = rid })
1596+nextConnInId :: Accessor ValidLocalEndPointState HeavyweightConnectionId
1597+nextConnInId = accessor _nextConnInId (\rid st -> st { _nextConnInId = rid })
15881598
15891599remoteOutgoing :: Accessor ValidRemoteEndPointState Int
15901600remoteOutgoing = accessor _remoteOutgoing (\cs conn -> conn { _remoteOutgoing = cs })
15911601
1592−remoteIncoming :: Accessor ValidRemoteEndPointState IntSet
1602+remoteIncoming :: Accessor ValidRemoteEndPointState (Set LightweightConnectionId)
15931603remoteIncoming = accessor _remoteIncoming (\cs conn -> conn { _remoteIncoming = cs })
15941604
1595−pendingCtrlRequests :: Accessor ValidRemoteEndPointState (IntMap (MVar (Either IOException [ByteString])))
1596−pendingCtrlRequests = accessor _pendingCtrlRequests (\rep st -> st { _pendingCtrlRequests = rep })
1597−
1598−nextCtrlRequestId :: Accessor ValidRemoteEndPointState ControlRequestId
1599−nextCtrlRequestId = accessor _nextCtrlRequestId (\cid st -> st { _nextCtrlRequestId = cid })
1605+remoteMaxIncoming :: Accessor ValidRemoteEndPointState LightweightConnectionId
1606+remoteMaxIncoming = accessor _remoteMaxIncoming (\lcid st -> st { _remoteMaxIncoming = lcid })
16001607
16011608localEndPointAt :: EndPointAddress -> Accessor ValidTransportState (Maybe LocalEndPoint)
16021609localEndPointAt addr = localEndPoints >>> DAC.mapMaybe addr
16031610
1604−pendingCtrlRequestsAt :: ControlRequestId -> Accessor ValidRemoteEndPointState (Maybe (MVar (Either IOException [ByteString])))
1605−pendingCtrlRequestsAt ix = pendingCtrlRequests >>> DAC.intMapMaybe (fromIntegral ix)
1606−
1607−localConnectionTo :: EndPointAddress
1608− -> Accessor ValidLocalEndPointState (Maybe RemoteEndPoint)
1611+localConnectionTo :: EndPointAddress -> Accessor ValidLocalEndPointState (Maybe RemoteEndPoint)
16091612localConnectionTo addr = localConnections >>> DAC.mapMaybe addr
16101613
16111614-------------------------------------------------------------------------------