@@ -85,7 +85,7 @@ import qualified Data.IntMap as IntMap (empty)
85import Data.IntSet (IntSet)
85import Data.IntSet (IntSet)
86import qualified Data.IntSet as IntSet (empty, insert, elems, singleton, null, delete)
86import qualified Data.IntSet as IntSet (empty, insert, elems, singleton, null, delete)
87import Data.Map (Map)
87import Data.Map (Map)
88−import qualified Data.Map as Map (empty, elems, size)
88+import qualified Data.Map as Map (empty, elems)
89import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
89import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
90import qualified Data.Accessor.Container as DAC (mapMaybe, intMapMaybe)
90import qualified Data.Accessor.Container as DAC (mapMaybe, intMapMaybe)
91import System.IO (hPutStrLn, stderr)
91import System.IO (hPutStrLn, stderr)
@@ -209,6 +209,7 @@ data TransportState =
210data ValidTransportState = ValidTransportState
210data ValidTransportState = ValidTransportState
211 { _localEndPoints :: Map EndPointAddress LocalEndPoint
211 { _localEndPoints :: Map EndPointAddress LocalEndPoint
212+ , _nextEndPointId :: EndPointId
214data LocalEndPoint = LocalEndPoint
215data LocalEndPoint = LocalEndPoint
@@ -330,7 +331,10 @@ createTransport host port = do
330-- | You should probably not use this function (used for unit testing only)
331-- | You should probably not use this function (used for unit testing only)
331createTransportExposeInternals :: N.HostName -> N.ServiceName -> IO (Either IOException (Transport, ThreadId))
332createTransportExposeInternals :: N.HostName -> N.ServiceName -> IO (Either IOException (Transport, ThreadId))
332createTransportExposeInternals host port = do
333createTransportExposeInternals host port = do
333− state <- newMVar . TransportValid $ ValidTransportState { _localEndPoints = Map.empty }
334+ state <- newMVar . TransportValid $ ValidTransportState
335+ { _localEndPoints = Map.empty
336+ , _nextEndPointId = 0
334 let transport = TCPTransport { transportState = state
338 let transport = TCPTransport { transportState = state
335 , transportHost = host
339 , transportHost = host
336 , transportPort = port
340 , transportPort = port
@@ -378,7 +382,6 @@ apiCloseTransport transport mTransportThread evs = do
378 -- transport thread died (in which case we don't need to kill it)
382 -- transport thread died (in which case we don't need to kill it)
379 return ()
383 return ()
382-- | Create a new endpoint
385-- | Create a new endpoint
383apiNewEndPoint :: TCPTransport -> IO (Either (TransportError NewEndPointErrorCode) EndPoint)
386apiNewEndPoint :: TCPTransport -> IO (Either (TransportError NewEndPointErrorCode) EndPoint)
384apiNewEndPoint transport = try $ do
387apiNewEndPoint transport = try $ do
@@ -418,8 +421,6 @@ apiConnect ourEndPoint theirAddress _reliability _hints = try $ do
418-- GUARANTEE: If the connection is alive on entry then the remote endpoint will
421-- GUARANTEE: If the connection is alive on entry then the remote endpoint will
419-- either be RemoteEndPointValid or RemoteEndPointClosing. Otherwise, the state
422-- either be RemoteEndPointValid or RemoteEndPointClosing. Otherwise, the state
420-- of the remote endpoint will not be changed.
423-- of the remote endpoint will not be changed.
422−-- TODO: We ignore errors during a close. Is that right?
423apiClose :: RemoteEndPoint -> ConnectionId -> IORef Bool -> IO ()
424apiClose :: RemoteEndPoint -> ConnectionId -> IORef Bool -> IO ()
424apiClose theirEndPoint connId connAlive = void . tryIO $ do
425apiClose theirEndPoint connId connAlive = void . tryIO $ do
425 modifyMVar_ (remoteState theirEndPoint) $ \st -> case st of
426 modifyMVar_ (remoteState theirEndPoint) $ \st -> case st of
@@ -428,6 +429,7 @@ apiClose theirEndPoint connId connAlive = void . tryIO $ do
428 if alive
429 if alive
430 writeIORef connAlive False
431 writeIORef connAlive False
432+ -- Possible exception caught top-level
431 sendOn vst [encodeInt32 CloseConnection, encodeInt32 connId]
433 sendOn vst [encodeInt32 CloseConnection, encodeInt32 connId]
432 return (RemoteEndPointValid . (remoteOutgoing ^: (\x -> x - 1)) $ vst)
434 return (RemoteEndPointValid . (remoteOutgoing ^: (\x -> x - 1)) $ vst)
@@ -454,7 +456,7 @@ apiSend theirEndPoint connId connAlive payload = do
454 then mapExceptionIO sendFailed $ sendOn vst (encodeInt32 connId : prependLength payload)
456 then mapExceptionIO sendFailed $ sendOn vst (encodeInt32 connId : prependLength payload)
455 else throw $ TransportError SendClosed "Connection closed"
457 else throw $ TransportError SendClosed "Connection closed"
456 RemoteEndPointClosed -> do
458 RemoteEndPointClosed -> do
457− return (Left $ TransportError SendClosed "Connection lost")
459+ return . Left $ TransportError SendClosed "Connection lost"
458 RemoteEndPointClosing _ _ ->
460 RemoteEndPointClosing _ _ ->
459 -- The only way for the endpoint to be in closing state, while a
461 -- The only way for the endpoint to be in closing state, while a
460 -- connection is still active, is for the application to call 'close'
462 -- connection is still active, is for the application to call 'close'
@@ -463,7 +465,7 @@ apiSend theirEndPoint connId connAlive payload = do
463 -- yet. Even if the remote endpoint comes back with "please don't
465 -- yet. Even if the remote endpoint comes back with "please don't
464 -- close", this still means that our *outgoing* connection has been
466 -- close", this still means that our *outgoing* connection has been
465 -- closed
467 -- closed
466− return (Left $ TransportError SendClosed "Connection lost")
468+ return . Left $ TransportError SendClosed "Connection lost"
467 RemoteEndPointInvalid _ ->
469 RemoteEndPointInvalid _ ->
468 error "apiSend RELY violation"
470 error "apiSend RELY violation"
@@ -520,12 +522,15 @@ connectToSelf :: LocalEndPoint -> IO (Either (TransportError ConnectErrorCode) C
520connectToSelf ourEndPoint = do
522connectToSelf ourEndPoint = do
521 -- Here connAlive must an MVar because it is not protected by another lock
523 -- Here connAlive must an MVar because it is not protected by another lock
522 connAlive <- newMVar True
524 connAlive <- newMVar True
523− -- TODO: catch exception
524− connId <- getNextConnectionId ourEndPoint
525+ mConnId <- tryIO (getNextConnectionId ourEndPoint)
528+ return . Left $ TransportError ConnectNotFound (show err)
525− writeChan ourChan (ConnectionOpened connId ReliableOrdered (localAddress ourEndPoint))
530+ writeChan ourChan (ConnectionOpened connId ReliableOrdered (localAddress ourEndPoint))
526− return . Right $ Connection { send = selfSend connAlive connId
531+ return . Right $ Connection { send = selfSend connAlive connId
527− , close = selfClose connAlive connId
532+ , close = selfClose connAlive connId
530 ourChan :: Chan Event
535 ourChan :: Chan Event
531 ourChan = localChannel ourEndPoint
536 ourChan = localChannel ourEndPoint
@@ -565,13 +570,13 @@ createLocalEndPoint transport = do
566 modifyMVar (transportState transport) $ \st -> case st of
571 modifyMVar (transportState transport) $ \st -> case st of
567 TransportValid vst -> do
572 TransportValid vst -> do
568− let ix = fromIntegral $ Map.size (vst ^. localEndPoints)
573+ let ix = vst ^. nextEndPointId
569 let addr = encodeEndPointAddress (transportHost transport) (transportPort transport) ix
574 let addr = encodeEndPointAddress (transportHost transport) (transportPort transport) ix
570 let localEndPoint = LocalEndPoint { localAddress = addr
575 let localEndPoint = LocalEndPoint { localAddress = addr
571 , localChannel = chan
576 , localChannel = chan
572 , localState = state
577 , localState = state
574− return (TransportValid . (localEndPointAt addr ^= Just localEndPoint) $ vst, localEndPoint)
579+ return (TransportValid . (localEndPointAt addr ^= Just localEndPoint) . (nextEndPointId ^= ix + 1) $ vst, localEndPoint)
575 TransportClosed ->
580 TransportClosed ->
576 throw (TransportError NewEndPointFailed "Transport closed")
581 throw (TransportError NewEndPointFailed "Transport closed")
@@ -644,8 +649,10 @@ requestConnectionTo ourEndPoint theirAddress = go
644 -- point)
649 -- point)
645 reply <- handle failureHandler $ doRemoteRequest (ourEndPoint, theirEndPoint) RequestConnectionId
650 reply <- handle failureHandler $ doRemoteRequest (ourEndPoint, theirEndPoint) RequestConnectionId
646 case decodeInt32 . BS.concat $ reply of
651 case decodeInt32 . BS.concat $ reply of
647− Nothing -> failureHandler (userError "Invalid integer")
653+ failureHandler (userError "Invalid integer")
648− Just cid -> return (theirEndPoint, cid)
655+ return (theirEndPoint, cid)
650 -- If this is a new endpoint, fork a thread to listen for incoming
657 -- If this is a new endpoint, fork a thread to listen for incoming
651 -- connections. We don't want to do this while we hold the lock, because
658 -- connections. We don't want to do this while we hold the lock, because
@@ -979,6 +986,8 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
979 -- Create a new connection
986 -- Create a new connection
980 createNewConnection :: ControlRequestId -> IO ()
987 createNewConnection :: ControlRequestId -> IO ()
981 createNewConnection reqId = do
988 createNewConnection reqId = do
989+ -- getNextConnectionId throws an exception if ourEndPoint is closed; but
990+ -- if this endpoint is closed, this thread will soon die anyway
982 newId <- getNextConnectionId ourEndPoint
991 newId <- getNextConnectionId ourEndPoint
983 modifyMVar_ theirState $ \st -> do
992 modifyMVar_ theirState $ \st -> do
984 vst <- case st of
993 vst <- case st of
@@ -1135,6 +1144,9 @@ firstNonReservedConnectionId = 1024
1135localEndPoints :: Accessor ValidTransportState (Map EndPointAddress LocalEndPoint)
1144localEndPoints :: Accessor ValidTransportState (Map EndPointAddress LocalEndPoint)
1136localEndPoints = accessor _localEndPoints (\es st -> st { _localEndPoints = es })
1145localEndPoints = accessor _localEndPoints (\es st -> st { _localEndPoints = es })
1147+nextEndPointId :: Accessor ValidTransportState EndPointId
1148+nextEndPointId = accessor _nextEndPointId (\eid st -> st { _nextEndPointId = eid })
1138pendingCtrlRequests :: Accessor ValidLocalEndPointState (IntMap (MVar [ByteString]))
1150pendingCtrlRequests :: Accessor ValidLocalEndPointState (IntMap (MVar [ByteString]))
1139pendingCtrlRequests = accessor _pendingCtrlRequests (\rep st -> st { _pendingCtrlRequests = rep })
1151pendingCtrlRequests = accessor _pendingCtrlRequests (\rep st -> st { _pendingCtrlRequests = rep })