@@ -73,7 +73,6 @@ import Control.Concurrent.MVar ( MVar
73import Control.Category ((>>>))
73import Control.Category ((>>>))
74import Control.Applicative ((<$>))
74import Control.Applicative ((<$>))
75import Control.Monad (when, unless)
75import Control.Monad (when, unless)
76−import Control.Monad.Error (ErrorT(..), runErrorT)
77import Control.Exception ( IOException
76import Control.Exception ( IOException
78 , SomeException
77 , SomeException
@@ -374,7 +373,7 @@ data TransportInternals = TransportInternals
374 -- | Find the socket between a local and a remote endpoint
373 -- | Find the socket between a local and a remote endpoint
375 , socketBetween :: EndPointAddress
374 , socketBetween :: EndPointAddress
376 -> EndPointAddress
375 -> EndPointAddress
377− -> IO (Either String N.Socket)
376+ -> IO N.Socket
380--------------------------------------------------------------------------------
379--------------------------------------------------------------------------------
@@ -626,39 +625,38 @@ connectToSelf ourEndPoint = try . mapIOException connectFailed $ do
626--------------------------------------------------------------------------------
625--------------------------------------------------------------------------------
628-- Find a socket between two endpoints
627-- Find a socket between two endpoints
629+-- Throws an IO exception if the socket could not be found.
629internalSocketBetween :: TCPTransport -- ^ Transport
630internalSocketBetween :: TCPTransport -- ^ Transport
630 -> EndPointAddress -- ^ Local endpoint
631 -> EndPointAddress -- ^ Local endpoint
631 -> EndPointAddress -- ^ Remote endpoint
632 -> EndPointAddress -- ^ Remote endpoint
632− -> IO (Either String N.Socket)
633+ -> IO N.Socket
633−internalSocketBetween transport ourAddress theirAddress = runErrorT $ do
634+internalSocketBetween transport ourAddress theirAddress = do
634− ourEndPoint <- ErrorT $
635− withMVar (transportState transport) $ \st -> case st of
635+ ourEndPoint <- withMVar (transportState transport) $ \st -> case st of
636 TransportClosed ->
636 TransportClosed ->
637− return . Left $ "Transport closed"
637+ throwIO $ userError "Transport closed"
638 TransportValid vst ->
638 TransportValid vst ->
639 case vst ^. localEndPointAt ourAddress of
639 case vst ^. localEndPointAt ourAddress of
640− Nothing -> return . Left $ "Local endpoint not found"
640+ Nothing -> throwIO $ userError "Local endpoint not found"
641− Just ep -> return . Right $ ep
641+ Just ep -> return ep
642− theirEndPoint <- ErrorT $
643− withMVar (localState ourEndPoint) $ \st -> case st of
642+ theirEndPoint <- withMVar (localState ourEndPoint) $ \st -> case st of
644 LocalEndPointClosed ->
643 LocalEndPointClosed ->
645− return . Left $ "Local endpoint closed"
644+ throwIO $ userError "Local endpoint closed"
646 LocalEndPointValid vst ->
645 LocalEndPointValid vst ->
647 case vst ^. localConnectionTo theirAddress of
646 case vst ^. localConnectionTo theirAddress of
648− Nothing -> return . Left $ "Remote endpoint not found"
647+ Nothing -> throwIO $ userError "Remote endpoint not found"
649− Just ep -> return . Right $ ep
648+ Just ep -> return ep
651− withMVar (remoteState theirEndPoint) $ \st -> case st of
649+ withMVar (remoteState theirEndPoint) $ \st -> case st of
652− RemoteEndPointInit _ ->
650+ RemoteEndPointInit _ ->
653− return . Left $ "Remote endpoint not yet initialized"
651+ throwIO $ userError "Remote endpoint not yet initialized"
654− RemoteEndPointValid vst ->
652+ RemoteEndPointValid vst ->
655− return . Right $ remoteSocket vst
653+ return $ remoteSocket vst
656− RemoteEndPointClosing _ vst ->
654+ RemoteEndPointClosing _ vst ->
657− return . Right $ remoteSocket vst
655+ return $ remoteSocket vst
658− RemoteEndPointClosed ->
656+ RemoteEndPointClosed ->
659− return . Left $ "Remote endpoint closed"
657+ throwIO $ userError "Remote endpoint closed"
660− RemoteEndPointInvalid _ ->
658+ RemoteEndPointInvalid _ ->
661− return . Left $ "Remote endpoint invalid"
659+ throwIO $ userError "Remote endpoint invalid"
663--------------------------------------------------------------------------------
661--------------------------------------------------------------------------------
664-- Lower level functionality --
662-- Lower level functionality --
@@ -708,35 +706,25 @@ createLocalEndPoint transport = do
708requestConnectionTo :: LocalEndPoint
706requestConnectionTo :: LocalEndPoint
709 -> EndPointAddress
707 -> EndPointAddress
710 -> IO (RemoteEndPoint, ConnectionId)
708 -> IO (RemoteEndPoint, ConnectionId)
711−requestConnectionTo ourEndPoint theirAddress = go
709+requestConnectionTo ourEndPoint theirAddress = do
710+ (theirEndPoint, isNew) <- mapIOException connectFailed $
711+ findRemoteEndPoint ourEndPoint theirAddress BlockOnInit
712+ (RemoteEndPointValid . (remoteOutgoing ^: (+ 1)))
714+ -- If this is a new endpoint, start the initialization process and wait
715+ forM_ isNew $ \resolved -> do
716+ void . forkEndPointThread ourEndPoint $ do
718+ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing
721+ reply <- mapIOException connectFailed $
722+ doRemoteRequest (ourEndPoint, theirEndPoint) RequestConnectionId
723+ return (theirEndPoint, decodeInt32 . BS.concat $ reply)
714− (theirEndPoint, isNew) <- mapIOException connectFailed $
715− findRemoteEndPoint ourEndPoint theirAddress True
716− (RemoteEndPointValid . (remoteOutgoing ^: (+ 1)))
718− -- If this is a new endpoint, start the initialization process and wait
719− forM_ isNew $ \resolved -> do
720− void . forkEndPointThread ourEndPoint $ do
722− setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing
725− reply <- mapIOException connectFailed $
726− doRemoteRequest (ourEndPoint, theirEndPoint) RequestConnectionId
727− case decodeInt32 . BS.concat $ reply of
729− throwIO (connectFailed $ userError "Invalid integer")
731− return (theirEndPoint, cid)
733 connectFailed = TransportError ConnectFailed . show
725 connectFailed = TransportError ConnectFailed . show
735-- | Set up a remote endpoint
727-- | Set up a remote endpoint
737−-- RELY: The state of the remote endpoint must be uninitialized.
738−-- GUARANTEE: Will only change the state to RemoteEndPointValid or
739−-- RemoteEndPointInvalid.
740setupRemoteEndPoint :: EndPointPair -- ^ Local endpoint, remote endpoint
728setupRemoteEndPoint :: EndPointPair -- ^ Local endpoint, remote endpoint
741 -> Int -- ^ Initial (outgoing) refcount
729 -> Int -- ^ Initial (outgoing) refcount
@@ -751,9 +739,7 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
751 , _remoteIncoming = IntSet.empty
739 , _remoteIncoming = IntSet.empty
752 , sendOn = sendMany sock
740 , sendOn = sendMany sock
754− modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
756− return (RemoteEndPointValid vst)
742+ resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointValid vst)
757 return True
743 return True
758 Right (sock, ConnectionRequestInvalid) -> do
744 Right (sock, ConnectionRequestInvalid) -> do
759 -- We remove the endpoint from our local state again because the next
745 -- We remove the endpoint from our local state again because the next
@@ -763,9 +749,7 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
763 -- connection requests.
749 -- connection requests.
764 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
750 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
765 let err = invalidAddress "Invalid endpoint"
751 let err = invalidAddress "Invalid endpoint"
766− modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
768− return (RemoteEndPointInvalid err)
752+ resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointInvalid err)
769 tryCloseSocket sock
753 tryCloseSocket sock
770 return False
754 return False
771 Right (sock, ConnectionRequestCrossed) -> do
755 Right (sock, ConnectionRequestCrossed) -> do
@@ -775,9 +759,7 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
775 return False
759 return False
776 Left err -> do
760 Left err -> do
777 removeRemoteEndPoint (ourEndPoint, theirEndPoint) -- See comment above
761 removeRemoteEndPoint (ourEndPoint, theirEndPoint) -- See comment above
778− modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
780− return (RemoteEndPointInvalid err)
762+ resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointInvalid err)
781 return False
763 return False
783 -- If we get to this point without an exception, then
765 -- If we get to this point without an exception, then
@@ -800,14 +782,12 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
800 onError result = do
782 onError result = do
801 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
783 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
802 case result of
784 case result of
803− Left err -> do
785+ Left err ->
804− tryPutMVar theirState (RemoteEndPointInvalid err)
786+ void $ tryPutMVar theirState (RemoteEndPointInvalid err)
806 Right (sock, _) -> do
787 Right (sock, _) -> do
807 let err = failed "setupRemoteEndPoint failed"
788 let err = failed "setupRemoteEndPoint failed"
808 tryPutMVar theirState (RemoteEndPointInvalid err)
789 tryPutMVar theirState (RemoteEndPointInvalid err)
809 tryCloseSocket sock
790 tryCloseSocket sock
812 failed = TransportError ConnectFailed
792 failed = TransportError ConnectFailed
813 ourAddress = localAddress ourEndPoint
793 ourAddress = localAddress ourEndPoint
@@ -815,6 +795,21 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
815 theirState = remoteState theirEndPoint
795 theirState = remoteState theirEndPoint
816 invalidAddress = TransportError ConnectNotFound
796 invalidAddress = TransportError ConnectNotFound
798+resolveInit :: EndPointPair -> RemoteState -> IO ()
799+resolveInit (ourEndPoint, theirEndPoint) newState =
800+ modifyMVar_ (remoteState theirEndPoint) $ \st -> case st of
801+ RemoteEndPointInvalid _ ->
802+ relyViolation (ourEndPoint, theirEndPoint) "resolveInit (invalid)"
803+ RemoteEndPointInit resolved -> do
806+ RemoteEndPointClosed ->
807+ throwIO $ userError "Remote endpoint closed"
808+ RemoteEndPointClosing _ _ ->
809+ relyViolation (ourEndPoint, theirEndPoint) "resolveInit (closing)"
810+ RemoteEndPointValid _ ->
811+ relyViolation (ourEndPoint, theirEndPoint) "resolveInit (valid)"
818-- | Establish a connection to a remote endpoint
813-- | Establish a connection to a remote endpoint
819socketToEndPoint :: EndPointAddress -- ^ Our address
814socketToEndPoint :: EndPointAddress -- ^ Our address
820 -> EndPointAddress -- ^ Their address
815 -> EndPointAddress -- ^ Their address
@@ -904,9 +899,6 @@ decodeEndPointAddress (EndPointAddress bs) =
905-- | Do a (blocking) remote request
900-- | Do a (blocking) remote request
907−-- RELY: Remote endpoint must be in valid or closed state.
908−-- GUARANTEE: Will not change the state of the remote endpoint.
910-- May throw IO (user) exception if the local or the remote endpoint is closed,
902-- May throw IO (user) exception if the local or the remote endpoint is closed,
911-- or if the send fails.
903-- or if the send fails.
912doRemoteRequest :: EndPointPair -> ControlHeader -> IO [ByteString]
904doRemoteRequest :: EndPointPair -> ControlHeader -> IO [ByteString]
@@ -1147,7 +1139,7 @@ handleConnectionRequest transport sock = handle handleException $ do
1147 go ourEndPoint theirAddress = do
1139 go ourEndPoint theirAddress = do
1148 mEndPoint <- handle ((>> return Nothing) . invalidEndPoint) $ do
1140 mEndPoint <- handle ((>> return Nothing) . invalidEndPoint) $ do
1149 (theirEndPoint, isNew) <-
1141 (theirEndPoint, isNew) <-
1150− findRemoteEndPoint ourEndPoint theirAddress False
1142+ findRemoteEndPoint ourEndPoint theirAddress DontBlockOnInit
1151 (throw $ userError "Already connected")
1143 (throw $ userError "Already connected")
1153 let ourAddress = localAddress ourEndPoint
1145 let ourAddress = localAddress ourEndPoint
@@ -1166,9 +1158,7 @@ handleConnectionRequest transport sock = handle handleException $ do
1166 , _remoteIncoming = IntSet.empty
1158 , _remoteIncoming = IntSet.empty
1167 , sendOn = sendMany sock
1159 , sendOn = sendMany sock
1169− modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
1170− putMVar resolved ()
1171− return (RemoteEndPointValid vst)
1161+ resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointValid vst)
1172 sendMany sock [encodeInt32 ConnectionRequestAccepted]
1162 sendMany sock [encodeInt32 ConnectionRequestAccepted]
1173 return (Just theirEndPoint)
1163 return (Just theirEndPoint)
1174 -- If we left the scope of the exception handler with a return value of
1164 -- If we left the scope of the exception handler with a return value of
@@ -1186,15 +1176,17 @@ handleConnectionRequest transport sock = handle handleException $ do
1186 tryIO $ sendMany sock [encodeInt32 ConnectionRequestInvalid]
1176 tryIO $ sendMany sock [encodeInt32 ConnectionRequestInvalid]
1187 tryCloseSocket sock
1177 tryCloseSocket sock
1179+data BlockOnInit = BlockOnInit | DontBlockOnInit
1189-- | Find a remote endpoint. If the remote endpoint does not yet exist we
1181-- | Find a remote endpoint. If the remote endpoint does not yet exist we
1190-- create it in Init state and return the corresponding 'resolved' MVar.
1182-- create it in Init state and return the corresponding 'resolved' MVar.
1191findRemoteEndPoint
1183findRemoteEndPoint
1192− :: LocalEndPoint -- ^ Local endpoint
1184+ :: LocalEndPoint
1193− -> EndPointAddress -- ^ Remote address
1185+ -> EndPointAddress
1194− -> Bool -- ^ Wait if remote endpoint is in Init state
1186+ -> BlockOnInit
1195 -> (ValidRemoteEndPointState -> RemoteState) -- ^ Apply if in Valid state
1187 -> (ValidRemoteEndPointState -> RemoteState) -- ^ Apply if in Valid state
1196 -> IO (RemoteEndPoint, Maybe (MVar ()))
1188 -> IO (RemoteEndPoint, Maybe (MVar ()))
1197−findRemoteEndPoint ourEndPoint theirAddress waitOnInit whenValid = go
1189+findRemoteEndPoint ourEndPoint theirAddress blockOnInit whenValid = go
1200 (theirEndPoint, isNew) <- modifyMVar ourState $ \st -> case st of
1192 (theirEndPoint, isNew) <- modifyMVar ourState $ \st -> case st of
@@ -1233,9 +1225,9 @@ findRemoteEndPoint ourEndPoint theirAddress waitOnInit whenValid = go
1233 RemoteEndPointInvalid err ->
1225 RemoteEndPointInvalid err ->
1234 throwIO err
1226 throwIO err
1235 RemoteEndPointInit resolved ->
1227 RemoteEndPointInit resolved ->
1236− if waitOnInit
1228+ case blockOnInit of
1237− then readMVar resolved >> go
1229+ BlockOnInit -> readMVar resolved >> go
1238− else return (theirEndPoint, Nothing)
1230+ DontBlockOnInit -> return (theirEndPoint, Nothing)
1239 RemoteEndPointValid _ ->
1231 RemoteEndPointValid _ ->
1240 return (theirEndPoint, Nothing)
1232 return (theirEndPoint, Nothing)
1241 RemoteEndPointClosing resolved _ ->
1233 RemoteEndPointClosing resolved _ ->
@@ -1248,9 +1240,6 @@ findRemoteEndPoint ourEndPoint theirAddress waitOnInit whenValid = go
1248-- | Handle requests from a remote endpoint.
1240-- | Handle requests from a remote endpoint.
1250-- Returns only if the remote party closes the socket or if an error occurs.
1242-- Returns only if the remote party closes the socket or if an error occurs.
1252−-- RELY: The remote endpoint must not be invalid.
1253−-- GUARANTEE: May change the remote endpoint to RemoteEndPointClosed state.
1254handleIncomingMessages :: EndPointPair -> IO ()
1243handleIncomingMessages :: EndPointPair -> IO ()
1255handleIncomingMessages (ourEndPoint, theirEndPoint) = do
1244handleIncomingMessages (ourEndPoint, theirEndPoint) = do
1256 mSock <- withMVar theirState $ \st ->
1245 mSock <- withMVar theirState $ \st ->