@@ -39,7 +39,6 @@ import Network.Transport.Internal ( encodeInt32
3939 , mapIOException
4040 , tryIO
4141 , tryToEnum
42− , tryModifyMVar
4342 , void
4443 )
4544import qualified Network.Socket as N ( HostName
@@ -108,6 +107,7 @@ import qualified Data.Map as Map (empty)
108107import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
109108import qualified Data.Accessor.Container as DAC (mapMaybe, intMapMaybe)
110109import Data.Foldable (forM_, mapM_)
110+import Data.Maybe (isNothing)
111111
112112-- $design
113113--
@@ -292,6 +292,7 @@ data RemoteEndPoint = RemoteEndPoint
292292
293293data RemoteState =
294294 RemoteEndPointInvalid (TransportError ConnectErrorCode)
295+ | RemoteEndPointInit (MVar ())
295296 | RemoteEndPointValid ValidRemoteEndPointState
296297 | RemoteEndPointClosing (MVar ()) ValidRemoteEndPointState
297298 | RemoteEndPointClosed
@@ -487,7 +488,9 @@ apiSend (ourEndPoint, theirEndPoint) connId connAlive payload =
487488 liftM join . try . mapIOException sendFailed $
488489 withRemoteState (ourEndPoint, theirEndPoint) RemoteStatePatternMatch
489490 { caseInvalid = \_ ->
490− relyViolation (ourEndPoint, theirEndPoint) "apiSend"
491+ relyViolation (ourEndPoint, theirEndPoint) "apiSend"
492+ , caseInit = \_ ->
493+ relyViolation (ourEndPoint, theirEndPoint) "apiSend"
491494 , caseValid = \vst -> do
492495 alive <- readIORef connAlive
493496 if alive
@@ -540,6 +543,8 @@ apiCloseEndPoint transport evs ourEndPoint = do
540543 case st of
541544 RemoteEndPointInvalid _ ->
542545 return st
546+ RemoteEndPointInit _ ->
547+ return st
543548 RemoteEndPointValid conn -> do
544549 -- Try to send a CloseSocket request
545550 tryIO $ sendOn conn [encodeInt32 CloseSocket]
@@ -621,6 +626,8 @@ internalSocketBetween transport ourAddress theirAddress = runErrorT $ do
621626 Just ep -> return . Right $ ep
622627 ErrorT $
623628 withMVar (remoteState theirEndPoint) $ \st -> case st of
629+ RemoteEndPointInit _ ->
630+ return . Left $ "Remote endpoint not yet initialized"
624631 RemoteEndPointValid vst ->
625632 return . Right $ remoteSocket vst
626633 RemoteEndPointClosing _ vst ->
@@ -682,12 +689,15 @@ requestConnectionTo ourEndPoint theirAddress = go
682689 where
683690 go = do
684691 (theirEndPoint, isNew) <- mapIOException connectFailed $
685− findRemoteEndPoint ourEndPoint theirAddress
686− (Just $ RemoteEndPointValid . (remoteOutgoing ^: (+ 1)))
692+ findRemoteEndPoint ourEndPoint theirAddress True
693+ (RemoteEndPointValid . (remoteOutgoing ^: (+ 1)))
687694
688− when isNew . void . forkEndPointThread ourEndPoint $ do
689− let outgoing = 1
690− setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing
695+ -- If this is a new endpoint, start the initialization process and wait
696+ forM_ isNew $ \resolved -> do
697+ void . forkEndPointThread ourEndPoint $ do
698+ let outgoing = 1
699+ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing
700+ readMVar resolved
691701
692702 reply <- mapIOException connectFailed $
693703 doRemoteRequest (ourEndPoint, theirEndPoint) RequestConnectionId
@@ -718,7 +728,9 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
718728 , _remoteIncoming = IntSet.empty
719729 , sendOn = sendMany sock
720730 }
721− putMVar theirState (RemoteEndPointValid vst)
731+ modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
732+ putMVar resolved ()
733+ return (RemoteEndPointValid vst)
722734 return True
723735 Right (sock, ConnectionRequestInvalid) -> do
724736 -- We remove the endpoint from our local state again because the next
@@ -728,15 +740,21 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
728740 -- connection requests.
729741 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
730742 let err = invalidAddress "Invalid endpoint"
731− putMVar theirState (RemoteEndPointInvalid err)
743+ modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
744+ putMVar resolved ()
745+ return (RemoteEndPointInvalid err)
732746 tryCloseSocket sock
733747 return False
734748 Right (sock, ConnectionRequestCrossed) -> do
749+ -- We leave the endpoint in Init state, handleConnectionRequest will
750+ -- take care of it
735751 tryCloseSocket sock
736752 return False
737753 Left err -> do
738754 removeRemoteEndPoint (ourEndPoint, theirEndPoint) -- See comment above
739− putMVar theirState (RemoteEndPointInvalid err)
755+ modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
756+ putMVar resolved ()
757+ return (RemoteEndPointInvalid err)
740758 return False
741759
742760 -- If we get to this point without an exception, then
@@ -884,6 +902,7 @@ doRemoteRequest (ourEndPoint, theirEndPoint) header = do
884902 throwIO (userError "Local endpoint closed")
885903 withRemoteState (ourEndPoint, theirEndPoint) RemoteStatePatternMatch
886904 { caseInvalid = throwIO
905+ , caseInit = \_ -> relyViolation (ourEndPoint, theirEndPoint) "doRemoteRequest (init)"
887906 , caseValid = \vst -> sendOn vst [encodeInt32 header, encodeInt32 reqId]
888907 , caseClosing = \_ _ -> relyViolation (ourEndPoint, theirEndPoint) "doRemoteRequest (closing)"
889908 , caseClosed = throwIO $ userError "Remote endpoint closed"
@@ -954,6 +973,7 @@ forkEndPointThread ourEndPoint p =
954973
955974data RemoteStatePatternMatch a = RemoteStatePatternMatch
956975 { caseInvalid :: TransportError ConnectErrorCode -> IO a
976+ , caseInit :: MVar () -> IO a
957977 , caseValid :: ValidRemoteEndPointState -> IO a
958978 , caseClosing :: MVar () -> ValidRemoteEndPointState -> IO a
959979 , caseClosed :: IO a
@@ -963,6 +983,7 @@ remoteStateIdentity :: RemoteStatePatternMatch RemoteState
963983remoteStateIdentity =
964984 RemoteStatePatternMatch
965985 { caseInvalid = return . RemoteEndPointInvalid
986+ , caseInit = return . RemoteEndPointInit
966987 , caseValid = return . RemoteEndPointValid
967988 , caseClosing = (return .) . RemoteEndPointClosing
968989 , caseClosed = return RemoteEndPointClosed
@@ -990,6 +1011,11 @@ modifyRemoteState (ourEndPoint, theirEndPoint) match =
9901011 throwIO ex
9911012 -- The other cases are less interesting, because unless the endpoint is
9921013 -- in Valid state we're not supposed to do any IO on it
1014+ RemoteEndPointInit resolved -> do
1015+ (st', a) <- onException (restore $ caseInit match resolved)
1016+ (putMVar theirState st)
1017+ putMVar theirState st'
1018+ return a
9931019 RemoteEndPointClosing resolved vst -> do
9941020 (st', a) <- onException (restore $ caseClosing match resolved vst)
9951021 (putMVar theirState st)
@@ -1027,6 +1053,7 @@ modifyRemoteState_ (ourEndPoint, theirEndPoint) match =
10271053 modifyRemoteState (ourEndPoint, theirEndPoint)
10281054 RemoteStatePatternMatch
10291055 { caseInvalid = u . caseInvalid match
1056+ , caseInit = u . caseInit match
10301057 , caseValid = u . caseValid match
10311058 , caseClosing = \resolved vst -> u $ caseClosing match resolved vst
10321059 , caseClosed = u $ caseClosed match
@@ -1045,6 +1072,9 @@ withRemoteState (ourEndPoint, theirEndPoint) match =
10451072 { caseInvalid = \err -> do
10461073 a <- caseInvalid match err
10471074 return (RemoteEndPointInvalid err, a)
1075+ , caseInit = \resolved -> do
1076+ a <- caseInit match resolved
1077+ return (RemoteEndPointInit resolved, a)
10481078 , caseValid = \vst -> do
10491079 a <- caseValid match vst
10501080 return (RemoteEndPointValid vst, a)
@@ -1094,9 +1124,13 @@ handleConnectionRequest transport sock = handle handleException $ do
10941124 go ourEndPoint theirAddress = do
10951125 mEndPoint <- handle ((>> return Nothing) . invalidEndPoint) $ do
10961126 (theirEndPoint, isNew) <-
1097− findRemoteEndPoint ourEndPoint theirAddress Nothing
1127+ findRemoteEndPoint ourEndPoint theirAddress False
1128+ (throw $ userError "Already connected")
1129+
1130+ let ourAddress = localAddress ourEndPoint
1131+ theirState = remoteState theirEndPoint
1132+ crossed = isNothing isNew && ourAddress < theirAddress
10981133
1099− let crossed = not isNew && localAddress ourEndPoint < theirAddress
11001134 if crossed
11011135 then do
11021136 tryIO $ sendMany sock [encodeInt32 ConnectionRequestCrossed]
@@ -1109,7 +1143,9 @@ handleConnectionRequest transport sock = handle handleException $ do
11091143 , _remoteIncoming = IntSet.empty
11101144 , sendOn = sendMany sock
11111145 }
1112− putMVar (remoteState theirEndPoint) (RemoteEndPointValid vst)
1146+ modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
1147+ putMVar resolved ()
1148+ return (RemoteEndPointValid vst)
11131149 sendMany sock [encodeInt32 ConnectionRequestAccepted]
11141150 return (Just theirEndPoint)
11151151 -- If we left the scope of the exception handler with a return value of
@@ -1127,71 +1163,64 @@ handleConnectionRequest transport sock = handle handleException $ do
11271163 tryIO $ sendMany sock [encodeInt32 ConnectionRequestInvalid]
11281164 tryCloseSocket sock
11291165
1130−-- | Find a remote endpoint. Pass 'Nothing' for the third argument to find an
1131−-- uninitialized remote endpoint; pass 'Just' a function to find a Valid remote
1132−-- endpoint (and modify its state).
1166+-- | Find a remote endpoint. If the remote endpoint does not yet exist we
1167+-- create it in Init state and return the corresponding 'resolved' MVar.
11331168findRemoteEndPoint
1134− :: LocalEndPoint
1135− -> EndPointAddress
1136− -> Maybe (ValidRemoteEndPointState -> RemoteState)
1137− -> IO (RemoteEndPoint, Bool)
1138−findRemoteEndPoint ourEndPoint theirAddress mCaseValid = go
1169+ :: LocalEndPoint -- ^ Local endpoint
1170+ -> EndPointAddress -- ^ Remote address
1171+ -> Bool -- ^ Wait if remote endpoint is in Init state
1172+ -> (ValidRemoteEndPointState -> RemoteState) -- ^ Apply if in Valid state
1173+ -> IO (RemoteEndPoint, Maybe (MVar ()))
1174+findRemoteEndPoint ourEndPoint theirAddress waitOnInit whenValid = go
11391175 where
11401176 go = do
11411177 (theirEndPoint, isNew) <- modifyMVar ourState $ \st -> case st of
1142− LocalEndPointValid vst ->
1143− case vst ^. localConnectionTo theirAddress of
1144− Just theirEndPoint ->
1145− return (st, (theirEndPoint, False))
1146− Nothing -> do
1147− theirState <- newEmptyMVar
1148− let theirEndPoint = RemoteEndPoint
1149− { remoteAddress = theirAddress
1150− , remoteState = theirState
1151− , remoteId = vst ^. nextRemoteId
1152− }
1153− return ( LocalEndPointValid
1154− . (localConnectionTo theirAddress ^= Just theirEndPoint)
1155− . (nextRemoteId ^: (+ 1))
1156− $ vst
1157− , (theirEndPoint, True)
1158− )
1178+ LocalEndPointValid vst -> case vst ^. localConnectionTo theirAddress of
1179+ Just theirEndPoint ->
1180+ return (st, (theirEndPoint, Nothing))
1181+ Nothing -> do
1182+ resolved <- newEmptyMVar
1183+ theirState <- newMVar (RemoteEndPointInit resolved)
1184+ let theirEndPoint = RemoteEndPoint
1185+ { remoteAddress = theirAddress
1186+ , remoteState = theirState
1187+ , remoteId = vst ^. nextRemoteId
1188+ }
1189+ return ( LocalEndPointValid
1190+ . (localConnectionTo theirAddress ^= Just theirEndPoint)
1191+ . (nextRemoteId ^: (+ 1))
1192+ $ vst
1193+ , (theirEndPoint, Just resolved)
1194+ )
11591195 LocalEndPointClosed ->
11601196 throwIO $ userError "Local endpoint closed"
1161−
1162− if isNew
1163− then
1164− return (theirEndPoint, True)
1165− else do
1197+
1198+ case isNew of
1199+ Just resolved ->
1200+ return (theirEndPoint, Just resolved)
1201+ Nothing -> do
11661202 let theirState = remoteState theirEndPoint
1167− snapshot <- modify theirState $ \whenValid st -> case st of
1168− RemoteEndPointInvalid _ ->
1169− return (st, st)
1203+ snapshot <- modifyMVar theirState $ \st -> case st of
11701204 RemoteEndPointValid vst -> do
1171− let st' = whenValid vst
1205+ let st' = whenValid vst
11721206 return (st', st')
1173− RemoteEndPointClosing _ _ ->
1174− return (st, st)
1175− RemoteEndPointClosed ->
1207+ _ ->
11761208 return (st, st)
11771209 case snapshot of
1178− Nothing ->
1179− return (theirEndPoint, False)
1180− Just (RemoteEndPointInvalid _) ->
1181− throwIO $ userError "Invalid endpoint"
1182− Just (RemoteEndPointClosing resolved _) ->
1210+ RemoteEndPointInvalid err ->
1211+ throwIO err
1212+ RemoteEndPointInit resolved ->
1213+ if waitOnInit
1214+ then readMVar resolved >> go
1215+ else return (theirEndPoint, Nothing)
1216+ RemoteEndPointValid _ ->
1217+ return (theirEndPoint, Nothing)
1218+ RemoteEndPointClosing resolved _ ->
11831219 readMVar resolved >> go
1184− Just RemoteEndPointClosed ->
1220+ RemoteEndPointClosed ->
11851221 go
1186− Just (RemoteEndPointValid _) ->
1187− return (theirEndPoint, False)
1188−
1189− ourState = localState ourEndPoint
1190− modify m io = case mCaseValid of
1191− Nothing ->
1192− tryModifyMVar m (io (throw $ userError "Unexpected valid state"))
1193− Just f ->
1194− Just <$> modifyMVar m (io f)
1222+
1223+ ourState = localState ourEndPoint
11951224
11961225-- | Handle requests from a remote endpoint.
11971226--
@@ -1204,7 +1233,9 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
12041233 mSock <- withMVar theirState $ \st ->
12051234 case st of
12061235 RemoteEndPointInvalid _ ->
1207− relyViolation (ourEndPoint, theirEndPoint) "handleIncomingMessages:initialization"
1236+ relyViolation (ourEndPoint, theirEndPoint) "handleIncomingMessages"
1237+ RemoteEndPointInit _ ->
1238+ relyViolation (ourEndPoint, theirEndPoint) "handleIncomingMessages"
12081239 RemoteEndPointValid ep ->
12091240 return . Just $ remoteSocket ep
12101241 RemoteEndPointClosing _ ep ->
@@ -1264,6 +1295,8 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
12641295 vst <- case st of
12651296 RemoteEndPointInvalid _ ->
12661297 relyViolation (ourEndPoint, theirEndPoint) "handleIncomingMessages:createNewConnection"
1298+ RemoteEndPointInit _ ->
1299+ relyViolation (ourEndPoint, theirEndPoint) "handleIncomingMessages:createNewConnection"
12671300 RemoteEndPointValid vst ->
12681301 return (remoteIncoming ^: IntSet.insert newId $ vst)
12691302 RemoteEndPointClosing resolved vst -> do
@@ -1314,6 +1347,8 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
13141347 modifyMVar_ theirState $ \st -> case st of
13151348 RemoteEndPointInvalid _ ->
13161349 relyViolation (ourEndPoint, theirEndPoint) "closeConnection"
1350+ RemoteEndPointInit _ ->
1351+ relyViolation (ourEndPoint, theirEndPoint) "closeConnection"
13171352 RemoteEndPointValid vst -> do
13181353 unless (IntSet.member cid (vst ^. remoteIncoming)) $
13191354 throwIO $ userError "Invalid CloseConnection"
@@ -1338,6 +1373,8 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
13381373 case st of
13391374 RemoteEndPointInvalid _ ->
13401375 relyViolation (ourEndPoint, theirEndPoint) "handleIncomingMessages:closeSocket"
1376+ RemoteEndPointInit _ ->
1377+ relyViolation (ourEndPoint, theirEndPoint) "handleIncomingMessages:closeSocket"
13411378 RemoteEndPointValid vst -> do
13421379 -- We regard a CloseSocket message as an (optimized) way for the
13431380 -- remote endpoint to indicate that all its connections to us are
@@ -1385,6 +1422,8 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
13851422 case st of
13861423 RemoteEndPointInvalid _ ->
13871424 relyViolation (ourEndPoint, theirEndPoint) "handleIncomingMessages:prematureExit"
1425+ RemoteEndPointInit _ ->
1426+ relyViolation (ourEndPoint, theirEndPoint) "handleIncomingMessages:prematureExit"
13881427 RemoteEndPointValid vst -> do
13891428 let code = EventConnectionLost
13901429 (remoteAddress theirEndPoint)