@@ -73,7 +73,6 @@ import Control.Concurrent.MVar ( MVar
7373import Control.Category ((>>>))
7474import Control.Applicative ((<$>))
7575import Control.Monad (when, unless)
76−import Control.Monad.Error (ErrorT(..), runErrorT)
7776import Control.Exception ( IOException
7877 , SomeException
7978 , handle
@@ -374,7 +373,7 @@ data TransportInternals = TransportInternals
374373 -- | Find the socket between a local and a remote endpoint
375374 , socketBetween :: EndPointAddress
376375 -> EndPointAddress
377− -> IO (Either String N.Socket)
376+ -> IO N.Socket
378377 }
379378
380379--------------------------------------------------------------------------------
@@ -626,39 +625,38 @@ connectToSelf ourEndPoint = try . mapIOException connectFailed $ do
626625--------------------------------------------------------------------------------
627626
628627-- Find a socket between two endpoints
628+--
629+-- Throws an IO exception if the socket could not be found.
629630internalSocketBetween :: TCPTransport -- ^ Transport
630631 -> EndPointAddress -- ^ Local endpoint
631632 -> EndPointAddress -- ^ Remote endpoint
632− -> IO (Either String N.Socket)
633−internalSocketBetween transport ourAddress theirAddress = runErrorT $ do
634− ourEndPoint <- ErrorT $
635− withMVar (transportState transport) $ \st -> case st of
633+ -> IO N.Socket
634+internalSocketBetween transport ourAddress theirAddress = do
635+ ourEndPoint <- withMVar (transportState transport) $ \st -> case st of
636636 TransportClosed ->
637− return . Left $ "Transport closed"
637+ throwIO $ userError "Transport closed"
638638 TransportValid vst ->
639639 case vst ^. localEndPointAt ourAddress of
640− Nothing -> return . Left $ "Local endpoint not found"
641− Just ep -> return . Right $ ep
642− theirEndPoint <- ErrorT $
643− withMVar (localState ourEndPoint) $ \st -> case st of
640+ Nothing -> throwIO $ userError "Local endpoint not found"
641+ Just ep -> return ep
642+ theirEndPoint <- withMVar (localState ourEndPoint) $ \st -> case st of
644643 LocalEndPointClosed ->
645− return . Left $ "Local endpoint closed"
644+ throwIO $ userError "Local endpoint closed"
646645 LocalEndPointValid vst ->
647646 case vst ^. localConnectionTo theirAddress of
648− Nothing -> return . Left $ "Remote endpoint not found"
649− Just ep -> return . Right $ ep
650− ErrorT $
651− withMVar (remoteState theirEndPoint) $ \st -> case st of
652− RemoteEndPointInit _ ->
653− return . Left $ "Remote endpoint not yet initialized"
654− RemoteEndPointValid vst ->
655− return . Right $ remoteSocket vst
656− RemoteEndPointClosing _ vst ->
657− return . Right $ remoteSocket vst
658− RemoteEndPointClosed ->
659− return . Left $ "Remote endpoint closed"
660− RemoteEndPointInvalid _ ->
661− return . Left $ "Remote endpoint invalid"
647+ Nothing -> throwIO $ userError "Remote endpoint not found"
648+ Just ep -> return ep
649+ withMVar (remoteState theirEndPoint) $ \st -> case st of
650+ RemoteEndPointInit _ ->
651+ throwIO $ userError "Remote endpoint not yet initialized"
652+ RemoteEndPointValid vst ->
653+ return $ remoteSocket vst
654+ RemoteEndPointClosing _ vst ->
655+ return $ remoteSocket vst
656+ RemoteEndPointClosed ->
657+ throwIO $ userError "Remote endpoint closed"
658+ RemoteEndPointInvalid _ ->
659+ throwIO $ userError "Remote endpoint invalid"
662660
663661--------------------------------------------------------------------------------
664662-- Lower level functionality --
@@ -708,35 +706,25 @@ createLocalEndPoint transport = do
708706requestConnectionTo :: LocalEndPoint
709707 -> EndPointAddress
710708 -> 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)))
713+
714+ -- If this is a new endpoint, start the initialization process and wait
715+ forM_ isNew $ \resolved -> do
716+ void . forkEndPointThread ourEndPoint $ do
717+ let outgoing = 1
718+ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing
719+ readMVar resolved
720+
721+ reply <- mapIOException connectFailed $
722+ doRemoteRequest (ourEndPoint, theirEndPoint) RequestConnectionId
723+ return (theirEndPoint, decodeInt32 . BS.concat $ reply)
712724 where
713− go = do
714− (theirEndPoint, isNew) <- mapIOException connectFailed $
715− findRemoteEndPoint ourEndPoint theirAddress True
716− (RemoteEndPointValid . (remoteOutgoing ^: (+ 1)))
717−
718− -- If this is a new endpoint, start the initialization process and wait
719− forM_ isNew $ \resolved -> do
720− void . forkEndPointThread ourEndPoint $ do
721− let outgoing = 1
722− setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing
723− readMVar resolved
724−
725− reply <- mapIOException connectFailed $
726− doRemoteRequest (ourEndPoint, theirEndPoint) RequestConnectionId
727− case decodeInt32 . BS.concat $ reply of
728− Nothing ->
729− throwIO (connectFailed $ userError "Invalid integer")
730− Just cid ->
731− return (theirEndPoint, cid)
732−
733725 connectFailed = TransportError ConnectFailed . show
734726
735727-- | Set up a remote endpoint
736−--
737−-- RELY: The state of the remote endpoint must be uninitialized.
738−-- GUARANTEE: Will only change the state to RemoteEndPointValid or
739−-- RemoteEndPointInvalid.
740728setupRemoteEndPoint :: EndPointPair -- ^ Local endpoint, remote endpoint
741729 -> Int -- ^ Initial (outgoing) refcount
742730 -> IO ()
@@ -751,9 +739,7 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
751739 , _remoteIncoming = IntSet.empty
752740 , sendOn = sendMany sock
753741 }
754− modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
755− putMVar resolved ()
756− return (RemoteEndPointValid vst)
742+ resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointValid vst)
757743 return True
758744 Right (sock, ConnectionRequestInvalid) -> do
759745 -- We remove the endpoint from our local state again because the next
@@ -763,9 +749,7 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
763749 -- connection requests.
764750 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
765751 let err = invalidAddress "Invalid endpoint"
766− modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
767− putMVar resolved ()
768− return (RemoteEndPointInvalid err)
752+ resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointInvalid err)
769753 tryCloseSocket sock
770754 return False
771755 Right (sock, ConnectionRequestCrossed) -> do
@@ -775,9 +759,7 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
775759 return False
776760 Left err -> do
777761 removeRemoteEndPoint (ourEndPoint, theirEndPoint) -- See comment above
778− modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
779− putMVar resolved ()
780− return (RemoteEndPointInvalid err)
762+ resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointInvalid err)
781763 return False
782764
783765 -- If we get to this point without an exception, then
@@ -800,14 +782,12 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
800782 onError result = do
801783 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
802784 case result of
803− Left err -> do
804− tryPutMVar theirState (RemoteEndPointInvalid err)
805− return ()
785+ Left err ->
786+ void $ tryPutMVar theirState (RemoteEndPointInvalid err)
806787 Right (sock, _) -> do
807788 let err = failed "setupRemoteEndPoint failed"
808789 tryPutMVar theirState (RemoteEndPointInvalid err)
809790 tryCloseSocket sock
810− return ()
811791
812792 failed = TransportError ConnectFailed
813793 ourAddress = localAddress ourEndPoint
@@ -815,6 +795,21 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) outgoing = do
815795 theirState = remoteState theirEndPoint
816796 invalidAddress = TransportError ConnectNotFound
817797
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
804+ putMVar resolved ()
805+ return newState
806+ RemoteEndPointClosed ->
807+ throwIO $ userError "Remote endpoint closed"
808+ RemoteEndPointClosing _ _ ->
809+ relyViolation (ourEndPoint, theirEndPoint) "resolveInit (closing)"
810+ RemoteEndPointValid _ ->
811+ relyViolation (ourEndPoint, theirEndPoint) "resolveInit (valid)"
812+
818813-- | Establish a connection to a remote endpoint
819814socketToEndPoint :: EndPointAddress -- ^ Our address
820815 -> EndPointAddress -- ^ Their address
@@ -904,9 +899,6 @@ decodeEndPointAddress (EndPointAddress bs) =
904899
905900-- | Do a (blocking) remote request
906901--
907−-- RELY: Remote endpoint must be in valid or closed state.
908−-- GUARANTEE: Will not change the state of the remote endpoint.
909−--
910902-- May throw IO (user) exception if the local or the remote endpoint is closed,
911903-- or if the send fails.
912904doRemoteRequest :: EndPointPair -> ControlHeader -> IO [ByteString]
@@ -1147,7 +1139,7 @@ handleConnectionRequest transport sock = handle handleException $ do
11471139 go ourEndPoint theirAddress = do
11481140 mEndPoint <- handle ((>> return Nothing) . invalidEndPoint) $ do
11491141 (theirEndPoint, isNew) <-
1150− findRemoteEndPoint ourEndPoint theirAddress False
1142+ findRemoteEndPoint ourEndPoint theirAddress DontBlockOnInit
11511143 (throw $ userError "Already connected")
11521144
11531145 let ourAddress = localAddress ourEndPoint
@@ -1166,9 +1158,7 @@ handleConnectionRequest transport sock = handle handleException $ do
11661158 , _remoteIncoming = IntSet.empty
11671159 , sendOn = sendMany sock
11681160 }
1169− modifyMVar_ theirState $ \(RemoteEndPointInit resolved) -> do
1170− putMVar resolved ()
1171− return (RemoteEndPointValid vst)
1161+ resolveInit (ourEndPoint, theirEndPoint) (RemoteEndPointValid vst)
11721162 sendMany sock [encodeInt32 ConnectionRequestAccepted]
11731163 return (Just theirEndPoint)
11741164 -- If we left the scope of the exception handler with a return value of
@@ -1186,15 +1176,17 @@ handleConnectionRequest transport sock = handle handleException $ do
11861176 tryIO $ sendMany sock [encodeInt32 ConnectionRequestInvalid]
11871177 tryCloseSocket sock
11881178
1179+data BlockOnInit = BlockOnInit | DontBlockOnInit
1180+
11891181-- | Find a remote endpoint. If the remote endpoint does not yet exist we
11901182-- create it in Init state and return the corresponding 'resolved' MVar.
11911183findRemoteEndPoint
1192− :: LocalEndPoint -- ^ Local endpoint
1193− -> EndPointAddress -- ^ Remote address
1194− -> Bool -- ^ Wait if remote endpoint is in Init state
1184+ :: LocalEndPoint
1185+ -> EndPointAddress
1186+ -> BlockOnInit
11951187 -> (ValidRemoteEndPointState -> RemoteState) -- ^ Apply if in Valid state
11961188 -> IO (RemoteEndPoint, Maybe (MVar ()))
1197−findRemoteEndPoint ourEndPoint theirAddress waitOnInit whenValid = go
1189+findRemoteEndPoint ourEndPoint theirAddress blockOnInit whenValid = go
11981190 where
11991191 go = do
12001192 (theirEndPoint, isNew) <- modifyMVar ourState $ \st -> case st of
@@ -1233,9 +1225,9 @@ findRemoteEndPoint ourEndPoint theirAddress waitOnInit whenValid = go
12331225 RemoteEndPointInvalid err ->
12341226 throwIO err
12351227 RemoteEndPointInit resolved ->
1236− if waitOnInit
1237− then readMVar resolved >> go
1238− else return (theirEndPoint, Nothing)
1228+ case blockOnInit of
1229+ BlockOnInit -> readMVar resolved >> go
1230+ DontBlockOnInit -> return (theirEndPoint, Nothing)
12391231 RemoteEndPointValid _ ->
12401232 return (theirEndPoint, Nothing)
12411233 RemoteEndPointClosing resolved _ ->
@@ -1248,9 +1240,6 @@ findRemoteEndPoint ourEndPoint theirAddress waitOnInit whenValid = go
12481240-- | Handle requests from a remote endpoint.
12491241--
12501242-- Returns only if the remote party closes the socket or if an error occurs.
1251−--
1252−-- RELY: The remote endpoint must not be invalid.
1253−-- GUARANTEE: May change the remote endpoint to RemoteEndPointClosed state.
12541243handleIncomingMessages :: EndPointPair -> IO ()
12551244handleIncomingMessages (ourEndPoint, theirEndPoint) = do
12561245 mSock <- withMVar theirState $ \st ->