git.iko.soy/distributed-process
on master help sign in
Better error handling when self-connecting
commit553015e2b181352880e9d162b8f5b2691f778999 authorEdsko de Vries <edsko@edsko.net> dateMon 28 May 2012, 12:12 (14 years ago) parent806436c
3 files changed+71−45
network-transport/src/Network/Transport/TCP.hs+40−39 network-transport/tests/TestTCP.hs−1 network-transport/tests/TestTransport.hs+31−5
network-transport/src/Network/Transport/TCP.hs+40−39
@@ -259,10 +259,10 @@ data ValidLocalEndPointState = ValidLocalEndPointState
259259-- | /----------------\
260260-- | | |
261261-- | v |
262−-- \---> Valid ----+---> Closing
263−-- | |
264−-- | v
265−-- \---> Closed
262+-- +---> Valid ----+---> Closing
263+-- | | |
264+-- | | v
265+-- \---------------+---> Closed
266266--
267267-- Init: There are two places where we create new remote endpoints: in
268268-- requestConnectionTo (in response to an API 'connect' call) and in
@@ -565,16 +565,15 @@ apiCloseEndPoint transport evs ourEndPoint = do
565565 case st of
566566 RemoteEndPointInvalid _ ->
567567 return st
568− RemoteEndPointInit _ ->
569− return st
568+ RemoteEndPointInit resolved -> do
569+ putMVar resolved ()
570+ return RemoteEndPointClosed
570571 RemoteEndPointValid conn -> do
571− -- Try to send a CloseSocket request
572572 tryIO $ sendOn conn [encodeInt32 CloseSocket]
573− -- .. but even if it fails, close the socket anyway
574573 tryCloseSocket (remoteSocket conn)
575574 return RemoteEndPointClosed
576− RemoteEndPointClosing _ conn -> do
577− -- TODO: should we signal on resolved here?
575+ RemoteEndPointClosing resolved conn -> do
576+ putMVar resolved ()
578577 tryCloseSocket (remoteSocket conn)
579578 return RemoteEndPointClosed
580579 RemoteEndPointClosed ->
@@ -583,42 +582,44 @@ apiCloseEndPoint transport evs ourEndPoint = do
583582-- | Special case of 'apiConnect': connect an endpoint to itself
584583connectToSelf :: LocalEndPoint
585584 -> IO (Either (TransportError ConnectErrorCode) Connection)
586−connectToSelf ourEndPoint = do
587− -- Here connAlive must an MVar because it is not protected by another lock
588− connAlive <- newMVar True
589− mConnId <- tryIO (getNextConnectionId ourEndPoint)
590− case mConnId of
591− Left err ->
592− return . Left $ TransportError ConnectNotFound (show err)
593− Right connId -> do
594− writeChan ourChan $
595− ConnectionOpened connId ReliableOrdered (localAddress ourEndPoint)
596− return . Right $ Connection
597− { send = selfSend connAlive connId
598− , close = selfClose connAlive connId
599− }
585+connectToSelf ourEndPoint = try . mapIOException connectFailed $ do
586+ connAlive <- newIORef True -- Protected by the local endpoint lock
587+ connId <- getNextConnectionId ourEndPoint
588+ writeChan ourChan $
589+ ConnectionOpened connId ReliableOrdered (localAddress ourEndPoint)
590+ return Connection
591+ { send = selfSend connAlive connId
592+ , close = selfClose connAlive connId
593+ }
600594 where
601− ourChan :: Chan Event
602− ourChan = localChannel ourEndPoint
603−
604− selfSend :: MVar Bool
595+ selfSend :: IORef Bool
605596 -> ConnectionId
606597 -> [ByteString]
607598 -> IO (Either (TransportError SendErrorCode) ())
608599 selfSend connAlive connId msg =
609− modifyMVar connAlive $ \alive ->
610− if alive
611− then do
612− writeChan ourChan (Received connId msg)
613− return (alive, Right ())
614− else
615− return (alive, Left (TransportError SendFailed "Connection closed"))
600+ try . withMVar ourState $ \st -> case st of
601+ LocalEndPointValid _ -> do
602+ alive <- readIORef connAlive
603+ if alive
604+ then writeChan ourChan (Received connId msg)
605+ else throwIO $ TransportError SendClosed "Connection closed"
606+ LocalEndPointClosed ->
607+ throwIO $ TransportError SendFailed "Endpoint closed"
616608
617− selfClose :: MVar Bool -> ConnectionId -> IO ()
609+ selfClose :: IORef Bool -> ConnectionId -> IO ()
618610 selfClose connAlive connId =
619− modifyMVar_ connAlive $ \alive -> do
620− when alive $ writeChan ourChan (ConnectionClosed connId)
621− return False
611+ withMVar ourState $ \st -> case st of
612+ LocalEndPointValid _ -> do
613+ alive <- readIORef connAlive
614+ when alive $ do
615+ writeChan ourChan (ConnectionClosed connId)
616+ writeIORef connAlive False
617+ LocalEndPointClosed ->
618+ return ()
619+
620+ ourChan = localChannel ourEndPoint
621+ ourState = localState ourEndPoint
622+ connectFailed = TransportError ConnectFailed . show
622623
623624--------------------------------------------------------------------------------
624625-- Functions from TransportInternals --
network-transport/tests/TestTCP.hs−1
@@ -754,4 +754,3 @@ main = do
754754 , ("InvalidCloseConnection", testInvalidCloseConnection nextPort)
755755 ]
756756 testTransport (either (Left . show) (Right) <$> nextPort >>= createTransport "127.0.0.1")
757− return ()
network-transport/tests/TestTransport.hs+31−5
@@ -533,6 +533,36 @@ testConnectToSelfTwice transport numPings = do
533533
534534 takeMVar done
535535
536+-- | Test that we self-connections no longer work once we close our endpoint
537+-- or our transport
538+testCloseSelf :: IO (Either String Transport) -> IO ()
539+testCloseSelf newTransport = do
540+ Right transport <- newTransport
541+ Right endpoint1 <- newEndPoint transport
542+ Right endpoint2 <- newEndPoint transport
543+ Right conn1 <- connect endpoint1 (address endpoint1) ReliableOrdered
544+ Right conn2 <- connect endpoint1 (address endpoint1) ReliableOrdered
545+ Right conn3 <- connect endpoint2 (address endpoint2) ReliableOrdered
546+
547+ -- Close the conneciton and try to send
548+ close conn1
549+ Left (TransportError SendClosed _) <- send conn1 ["ping"]
550+
551+ -- Close the first endpoint. We should not be able to use the first
552+ -- connection anymore, or open more self connections, but the self connection
553+ -- to the second endpoint should still be fine
554+ closeEndPoint endpoint1
555+ Left (TransportError SendFailed _) <- send conn2 ["ping"]
556+ Left (TransportError ConnectFailed _) <- connect endpoint1 (address endpoint1) ReliableOrdered
557+ Right () <- send conn3 ["ping"]
558+
559+ -- Close the transport; now the second should no longer work
560+ closeTransport transport
561+ Left (TransportError SendFailed _) <- send conn3 ["ping"]
562+ Left (TransportError ConnectFailed _) <- connect endpoint2 (address endpoint2) ReliableOrdered
563+
564+ return ()
565+
536566-- | Test various aspects of 'closeEndPoint'
537567testCloseEndPoint :: Transport -> Int -> IO ()
538568testCloseEndPoint transport _ = do
@@ -726,13 +756,8 @@ testConnectClosedEndPoint transport = do
726756 Right endpoint <- newEndPoint transport
727757 readMVar serverClosed
728758
729− -- Connect to a remote closed endpoint
730759 Left (TransportError ConnectNotFound _) <- readMVar serverAddr >>= \addr -> connect endpoint addr ReliableOrdered
731760
732− -- Self-connect to a closed endpoint
733− closeEndPoint endpoint
734− Left (TransportError ConnectNotFound _) <- connect endpoint (address endpoint) ReliableOrdered
735−
736761 putMVar clientDone ()
737762
738763 takeMVar clientDone
@@ -850,6 +875,7 @@ testTransport newTransport = do
850875 , ("CloseTwice", testCloseTwice transport 100)
851876 , ("ConnectToSelf", testConnectToSelf transport numPings)
852877 , ("ConnectToSelfTwice", testConnectToSelfTwice transport numPings)
878+ , ("CloseSelf", testCloseSelf newTransport)
853879 , ("CloseEndPoint", testCloseEndPoint transport numPings)
854880 , ("CloseTransport", testCloseTransport newTransport)
855881 , ("ConnectClosedEndPoint", testConnectClosedEndPoint transport)