@@ -72,7 +72,7 @@ import Control.Concurrent.MVar ( MVar
73import Control.Category ((>>>))
73import Control.Category ((>>>))
74import Control.Applicative ((<$>))
74import Control.Applicative ((<$>))
75−import Control.Monad (forM, forM_, when, unless)
75+import Control.Monad (forM_, when, unless)
76import Control.Exception (IOException, SomeException, handle, throw, try, bracketOnError)
76import Control.Exception (IOException, SomeException, handle, throw, try, bracketOnError)
77import Data.IORef (IORef, newIORef, writeIORef, readIORef)
77import Data.IORef (IORef, newIORef, writeIORef, readIORef)
78import Data.ByteString (ByteString)
78import Data.ByteString (ByteString)
@@ -82,7 +82,7 @@ import Data.Int (Int32)
82import Data.IntMap (IntMap)
82import Data.IntMap (IntMap)
83import qualified Data.IntMap as IntMap (empty)
83import qualified Data.IntMap as IntMap (empty)
84import Data.IntSet (IntSet)
84import Data.IntSet (IntSet)
85−import qualified Data.IntSet as IntSet (empty, insert, elems, singleton, null, delete, unions)
85+import qualified Data.IntSet as IntSet (empty, insert, elems, singleton, null, delete)
86import Data.Map (Map)
86import Data.Map (Map)
87import qualified Data.Map as Map (empty, elems, size)
87import qualified Data.Map as Map (empty, elems, size)
88import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
88import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
@@ -279,7 +279,7 @@ data RemoteEndPoint = RemoteEndPoint
281data RemoteState =
281data RemoteState =
282− RemoteEndPointInvalid (FailedWith ConnectErrorCode)
282+ RemoteEndPointInvalid (TransportError ConnectErrorCode)
283 | RemoteEndPointValid ValidRemoteEndPointState
283 | RemoteEndPointValid ValidRemoteEndPointState
284 | RemoteEndPointClosing (MVar ()) ValidRemoteEndPointState
284 | RemoteEndPointClosing (MVar ()) ValidRemoteEndPointState
285 | RemoteEndPointClosed
285 | RemoteEndPointClosed
@@ -335,6 +335,7 @@ createTransport host port = do
336 terminationHandler :: TCPTransport -> SomeException -> IO ()
336 terminationHandler :: TCPTransport -> SomeException -> IO ()
337 terminationHandler transport _ = do
337 terminationHandler transport _ = do
338+ -- TODO: we currently don't make a distinction between endpoint failure and manual closure
338 mTSt <- modifyMVar (transportState transport) $ \st -> case st of
339 mTSt <- modifyMVar (transportState transport) $ \st -> case st of
339 TransportValid vst -> return (TransportClosed, Just vst)
340 TransportValid vst -> return (TransportClosed, Just vst)
340 TransportClosed -> return (TransportClosed, Nothing)
341 TransportClosed -> return (TransportClosed, Nothing)
@@ -350,7 +351,7 @@ createTransport host port = do
350--------------------------------------------------------------------------------
351--------------------------------------------------------------------------------
352-- | Create a new endpoint
353-- | Create a new endpoint
353−apiNewEndPoint :: TCPTransport -> IO (Either (FailedWith NewEndPointErrorCode) EndPoint)
354+apiNewEndPoint :: TCPTransport -> IO (Either (TransportError NewEndPointErrorCode) EndPoint)
354apiNewEndPoint transport = try $ do
355apiNewEndPoint transport = try $ do
355 ourEndPoint <- createLocalEndPoint transport
356 ourEndPoint <- createLocalEndPoint transport
356 return EndPoint
357 return EndPoint
@@ -362,14 +363,14 @@ apiNewEndPoint transport = try $ do
362 , resolveMulticastGroup = return . Left . const resolveMulticastGroupError
363 , resolveMulticastGroup = return . Left . const resolveMulticastGroupError
365− newMulticastGroupError = FailedWith NewMulticastGroupUnsupported "TCP does not support multicast"
366+ newMulticastGroupError = TransportError NewMulticastGroupUnsupported "TCP does not support multicast"
366− resolveMulticastGroupError = FailedWith ResolveMulticastGroupUnsupported "TCP does not support multicast"
367+ resolveMulticastGroupError = TransportError ResolveMulticastGroupUnsupported "TCP does not support multicast"
368-- | Connnect to an endpoint
369-- | Connnect to an endpoint
369apiConnect :: LocalEndPoint -- ^ Local end point
370apiConnect :: LocalEndPoint -- ^ Local end point
370 -> EndPointAddress -- ^ Remote address
371 -> EndPointAddress -- ^ Remote address
371 -> Reliability -- ^ Reliability (ignored)
372 -> Reliability -- ^ Reliability (ignored)
372− -> IO (Either (FailedWith ConnectErrorCode) Connection)
373+ -> IO (Either (TransportError ConnectErrorCode) Connection)
373apiConnect ourEndPoint theirAddress _ | localAddress ourEndPoint == theirAddress =
374apiConnect ourEndPoint theirAddress _ | localAddress ourEndPoint == theirAddress =
374 connectToSelf ourEndPoint
375 connectToSelf ourEndPoint
375apiConnect ourEndPoint theirAddress _ = try $ do
376apiConnect ourEndPoint theirAddress _ = try $ do
@@ -413,23 +414,23 @@ apiClose theirEndPoint connId connAlive = void . tryIO $ do
414-- RELY: The remote endpoint must be in state 'RemoteEndPointValid' or 'RemoteEndPointClosed'
415-- RELY: The remote endpoint must be in state 'RemoteEndPointValid' or 'RemoteEndPointClosed'
415-- GUARANTEE: The state of the remote endpoint will not be changed.
416-- GUARANTEE: The state of the remote endpoint will not be changed.
416−apiSend :: RemoteEndPoint -> ConnectionId -> IORef Bool -> [ByteString] -> IO (Either (FailedWith SendErrorCode) ())
417+apiSend :: RemoteEndPoint -> ConnectionId -> IORef Bool -> [ByteString] -> IO (Either (TransportError SendErrorCode) ())
417apiSend theirEndPoint connId connAlive payload = do
418apiSend theirEndPoint connId connAlive payload = do
418 withMVar (remoteState theirEndPoint) $ \st -> case st of
419 withMVar (remoteState theirEndPoint) $ \st -> case st of
419 RemoteEndPointValid vst -> do
420 RemoteEndPointValid vst -> do
420 alive <- readIORef connAlive
421 alive <- readIORef connAlive
421 try $ if alive
422 try $ if alive
422 then mapExceptionIO sendFailed $ sendOn vst (encodeInt32 connId : prependLength payload)
423 then mapExceptionIO sendFailed $ sendOn vst (encodeInt32 connId : prependLength payload)
423− else throw $ FailedWith SendConnectionClosed "Connection closed"
424+ else throw $ TransportError SendFailed "Connection closed"
424 RemoteEndPointClosed -> do
425 RemoteEndPointClosed -> do
425− return (Left $ FailedWith SendConnectionClosed "Endpoint closed")
426+ return (Left $ TransportError SendFailed "Endpoint closed")
426 RemoteEndPointClosing _ _ ->
427 RemoteEndPointClosing _ _ ->
427 error "apiSend RELY violation"
428 error "apiSend RELY violation"
428 RemoteEndPointInvalid _ ->
429 RemoteEndPointInvalid _ ->
429 error "apiSend RELY violation"
430 error "apiSend RELY violation"
431− sendFailed :: IOException -> FailedWith SendErrorCode
432+ sendFailed :: IOException -> TransportError SendErrorCode
432− sendFailed = FailedWith SendFailed . show
433+ sendFailed = TransportError SendFailed . show
434-- | Force-close the endpoint
435-- | Force-close the endpoint
435apiCloseEndPoint :: TCPTransport -> LocalEndPoint -> IO ()
436apiCloseEndPoint :: TCPTransport -> LocalEndPoint -> IO ()
@@ -448,37 +449,37 @@ apiCloseEndPoint transport ourEndPoint = do
448 return ()
449 return ()
449 Just vst -> do
450 Just vst -> do
450 -- Close all endpoints and kill all threads
451 -- Close all endpoints and kill all threads
451− incoming <- IntSet.elems . IntSet.unions <$> (forM (Map.elems $ vst ^. localConnections) $ tryCloseRemoteSocket)
452+ forM_ (Map.elems $ vst ^. localConnections) $ tryCloseRemoteSocket
452 forM_ (vst ^. internalThreads) killThread
453 forM_ (vst ^. internalThreads) killThread
453 -- We send a single message to the endpoint that it is closed. Subsequent
454 -- We send a single message to the endpoint that it is closed. Subsequent
454 -- calls will block. We could change this so that all subsequent calls to
455 -- calls will block. We could change this so that all subsequent calls to
455 -- receive return an error, but this would mean checking for some state on
456 -- receive return an error, but this would mean checking for some state on
456 -- every call to receive, which is an unnecessary overhead.
457 -- every call to receive, which is an unnecessary overhead.
457− writeChan (localChannel ourEndPoint) (ErrorEvent $ ErrorEventEndPointClosed incoming)
458+ writeChan (localChannel ourEndPoint) EndPointClosed
459 -- Close the remote socket and return the set of all incoming connections
460 -- Close the remote socket and return the set of all incoming connections
460− tryCloseRemoteSocket :: RemoteEndPoint -> IO IntSet
461+ tryCloseRemoteSocket :: RemoteEndPoint -> IO ()
461 tryCloseRemoteSocket theirEndPoint = do
462 tryCloseRemoteSocket theirEndPoint = do
462 -- We make an attempt to close the connection nicely (by sending a CloseSocket first)
463 -- We make an attempt to close the connection nicely (by sending a CloseSocket first)
463− modifyMVar (remoteState theirEndPoint) $ \st ->
464+ modifyMVar_ (remoteState theirEndPoint) $ \st ->
464 case st of
465 case st of
465 RemoteEndPointInvalid _ ->
466 RemoteEndPointInvalid _ ->
466− return (st, IntSet.empty)
467+ return st
467 RemoteEndPointValid conn -> do
468 RemoteEndPointValid conn -> do
468 -- Try to send a CloseSocket request
469 -- Try to send a CloseSocket request
469 tryIO $ sendOn conn [encodeInt32 CloseSocket]
470 tryIO $ sendOn conn [encodeInt32 CloseSocket]
470 -- .. but even if it fails, close the socket anyway
471 -- .. but even if it fails, close the socket anyway
471 -- (hence, two separate calls to tryIO)
472 -- (hence, two separate calls to tryIO)
472 tryIO $ N.sClose (remoteSocket conn)
473 tryIO $ N.sClose (remoteSocket conn)
473− return (RemoteEndPointClosed, conn ^. remoteIncoming)
474+ return RemoteEndPointClosed
474 RemoteEndPointClosing _ conn -> do
475 RemoteEndPointClosing _ conn -> do
475 tryIO $ N.sClose (remoteSocket conn)
476 tryIO $ N.sClose (remoteSocket conn)
476− return (RemoteEndPointClosed, IntSet.empty)
477+ return RemoteEndPointClosed
477 RemoteEndPointClosed ->
478 RemoteEndPointClosed ->
478− return (RemoteEndPointClosed, IntSet.empty)
479+ return RemoteEndPointClosed
480-- | Special case of 'apiConnect': connect an endpoint to itself
481-- | Special case of 'apiConnect': connect an endpoint to itself
481−connectToSelf :: LocalEndPoint -> IO (Either (FailedWith ConnectErrorCode) Connection)
482+connectToSelf :: LocalEndPoint -> IO (Either (TransportError ConnectErrorCode) Connection)
482connectToSelf ourEndPoint = do
483connectToSelf ourEndPoint = do
483 -- Here connAlive must an MVar because it is not protected by another lock
484 -- Here connAlive must an MVar because it is not protected by another lock
484 connAlive <- newMVar True
485 connAlive <- newMVar True
@@ -492,7 +493,7 @@ connectToSelf ourEndPoint = do
492 ourChan :: Chan Event
493 ourChan :: Chan Event
493 ourChan = localChannel ourEndPoint
494 ourChan = localChannel ourEndPoint
495− selfSend :: MVar Bool -> ConnectionId -> [ByteString] -> IO (Either (FailedWith SendErrorCode) ())
496+ selfSend :: MVar Bool -> ConnectionId -> [ByteString] -> IO (Either (TransportError SendErrorCode) ())
496 selfSend connAlive connId msg = do
497 selfSend connAlive connId msg = do
497 modifyMVar connAlive $ \alive ->
498 modifyMVar connAlive $ \alive ->
@@ -500,7 +501,7 @@ connectToSelf ourEndPoint = do
500 writeChan ourChan (Received connId msg)
501 writeChan ourChan (Received connId msg)
501 return (alive, Right ())
502 return (alive, Right ())
503− return (alive, Left (FailedWith SendConnectionClosed "Connection closed"))
504+ return (alive, Left (TransportError SendFailed "Connection closed"))
505 selfClose :: MVar Bool -> ConnectionId -> IO ()
506 selfClose :: MVar Bool -> ConnectionId -> IO ()
506 selfClose connAlive connId = do
507 selfClose connAlive connId = do
@@ -514,7 +515,7 @@ connectToSelf ourEndPoint = do
515-- | Create a new local endpoint
516-- | Create a new local endpoint
517−-- May throw a FailedWith NewEndPointErrorCode exception if the transport is closed.
518+-- May throw a TransportError NewEndPointErrorCode exception if the transport is closed.
518createLocalEndPoint :: TCPTransport -> IO LocalEndPoint
519createLocalEndPoint :: TCPTransport -> IO LocalEndPoint
519createLocalEndPoint transport = do
520createLocalEndPoint transport = do
520 chan <- newChan
521 chan <- newChan
@@ -535,7 +536,7 @@ createLocalEndPoint transport = do
536 return (TransportValid . (localEndPointAt addr ^= Just localEndPoint) $ vst, localEndPoint)
537 return (TransportValid . (localEndPointAt addr ^= Just localEndPoint) $ vst, localEndPoint)
537 TransportClosed ->
538 TransportClosed ->
538− throw (FailedWith NewEndPointTransportFailure "Transport closed")
539+ throw (TransportError NewEndPointFailed "Transport closed")
540-- | Request a connection to a remote endpoint
541-- | Request a connection to a remote endpoint
@@ -543,7 +544,7 @@ createLocalEndPoint transport = do
543-- the remote endpoint was in 'RemoteEndPointClosing' state then we will
544-- the remote endpoint was in 'RemoteEndPointClosing' state then we will
544-- additionally block until that is resolved.
545-- additionally block until that is resolved.
546−-- May throw a FailedWith ConnectErrorCode exception.
547+-- May throw a TransportError ConnectErrorCode exception.
547requestConnectionTo :: LocalEndPoint
548requestConnectionTo :: LocalEndPoint
548 -> EndPointAddress
549 -> EndPointAddress
549 -> IO (RemoteEndPoint, ConnectionId)
550 -> IO (RemoteEndPoint, ConnectionId)
@@ -599,7 +600,7 @@ requestConnectionTo ourEndPoint theirAddress = go
599 modifyMVar_ theirState $ \(RemoteEndPointValid ep) ->
600 modifyMVar_ theirState $ \(RemoteEndPointValid ep) ->
600 return (RemoteEndPointValid . (remoteOutgoing ^: (+ 1)) $ ep)
601 return (RemoteEndPointValid . (remoteOutgoing ^: (+ 1)) $ ep)
601 -- TODO: should we call closeIfUnused here?
602 -- TODO: should we call closeIfUnused here?
602− throw $ FailedWith ConnectFailed (show err)
603+ throw $ TransportError ConnectFailed (show err)
604 -- Do the actual connection request. This blocks until the remote
605 -- Do the actual connection request. This blocks until the remote
605 -- endpoint replies (but note that we don't hold any locks at this
606 -- endpoint replies (but note that we don't hold any locks at this
@@ -617,7 +618,7 @@ requestConnectionTo ourEndPoint theirAddress = go
617 findTheirEndPoint = do
618 findTheirEndPoint = do
618 (theirEndPoint, isNew) <- modifyMVar ourState $ \st -> case st of
619 (theirEndPoint, isNew) <- modifyMVar ourState $ \st -> case st of
619 LocalEndPointClosed ->
620 LocalEndPointClosed ->
620− throw (FailedWith ConnectFailed "Local endpoint closed")
621+ throw (TransportError ConnectFailed "Local endpoint closed")
621 LocalEndPointValid vst ->
622 LocalEndPointValid vst ->
622 case vst ^. localConnectionTo theirAddress of
623 case vst ^. localConnectionTo theirAddress of
623 Just theirEndPoint ->
624 Just theirEndPoint ->
@@ -674,21 +675,21 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) = do
674 putMVar theirState (RemoteEndPointInvalid err)
675 putMVar theirState (RemoteEndPointInvalid err)
676 theirState = remoteState theirEndPoint
677 theirState = remoteState theirEndPoint
677− invalidAddress = FailedWith ConnectInvalidAddress
678+ invalidAddress = TransportError ConnectNotFound
679-- | Establish a connection to a remote endpoint
680-- | Establish a connection to a remote endpoint
680socketToEndPoint :: EndPointAddress -- ^ Our address
681socketToEndPoint :: EndPointAddress -- ^ Our address
681 -> EndPointAddress -- ^ Their address
682 -> EndPointAddress -- ^ Their address
682− -> IO (Either (FailedWith ConnectErrorCode) (N.Socket, ConnectionRequestResponse))
683+ -> IO (Either (TransportError ConnectErrorCode) (N.Socket, ConnectionRequestResponse))
683socketToEndPoint (EndPointAddress ourAddress) theirAddress = try $ do
684socketToEndPoint (EndPointAddress ourAddress) theirAddress = try $ do
684 (host, port, theirEndPointId) <- case decodeEndPointAddress theirAddress of
685 (host, port, theirEndPointId) <- case decodeEndPointAddress theirAddress of
685− Nothing -> throw (invalidAddress . userError $ "Could not parse")
686+ Nothing -> throw (failed . userError $ "Could not parse")
686 Just dec -> return dec
687 Just dec -> return dec
687 addr:_ <- mapExceptionIO invalidAddress $ N.getAddrInfo Nothing (Just host) (Just port)
688 addr:_ <- mapExceptionIO invalidAddress $ N.getAddrInfo Nothing (Just host) (Just port)
688 bracketOnError (createSocket addr) N.sClose $ \sock -> do
689 bracketOnError (createSocket addr) N.sClose $ \sock -> do
690+ mapExceptionIO failed $ N.setSocketOption sock N.ReuseAddr 1
691+ mapExceptionIO invalidAddress $ N.connect sock (N.addrAddress addr)
689 response <- mapExceptionIO failed $ do
692 response <- mapExceptionIO failed $ do
690− N.setSocketOption sock N.ReuseAddr 1
691− N.connect sock (N.addrAddress addr)
692 sendMany sock (encodeInt32 theirEndPointId : prependLength [ourAddress])
693 sendMany sock (encodeInt32 theirEndPointId : prependLength [ourAddress])
693 recvInt32 sock
694 recvInt32 sock
694 case tryToEnum response of
695 case tryToEnum response of
@@ -701,10 +702,10 @@ socketToEndPoint (EndPointAddress ourAddress) theirAddress = try $ do
701 -- putStrLn $ "Created client socket " ++ show sock
702 -- putStrLn $ "Created client socket " ++ show sock
702 return sock
703 return sock
704− invalidAddress, insufficientResources, failed :: IOException -> FailedWith ConnectErrorCode
705+ invalidAddress, insufficientResources, failed :: IOException -> TransportError ConnectErrorCode
705− invalidAddress = FailedWith ConnectInvalidAddress . show
706+ invalidAddress = TransportError ConnectNotFound . show
706− insufficientResources = FailedWith ConnectInsufficientResources . show
707+ insufficientResources = TransportError ConnectInsufficientResources . show
707− failed = FailedWith ConnectFailed . show
708+ failed = TransportError ConnectFailed . show
709-- | Remove reference to a remote endpoint from a local endpoint
710-- | Remove reference to a remote endpoint from a local endpoint
@@ -1049,7 +1050,7 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
1050 -- Deal with a premature exit
1051 -- Deal with a premature exit
1051 prematureExit :: N.Socket -> IOException -> IO ()
1052 prematureExit :: N.Socket -> IOException -> IO ()
1052− prematureExit sock _ = do
1053+ prematureExit sock err = do
1053 N.sClose sock
1054 N.sClose sock
1054 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
1055 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
1055 mUnclosedConnections <- modifyMVar theirState $ \st ->
1056 mUnclosedConnections <- modifyMVar theirState $ \st ->
@@ -1069,7 +1070,7 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
1069 Nothing ->
1070 Nothing ->
1070 return ()
1071 return ()
1071 Just unclosedConnections -> writeChan ourChannel . ErrorEvent $
1072 Just unclosedConnections -> writeChan ourChannel . ErrorEvent $
1072− ErrorEventConnectionLost (remoteAddress theirEndPoint) (IntSet.elems unclosedConnections)
1073+ TransportError (EventConnectionLost (remoteAddress theirEndPoint) (IntSet.elems unclosedConnections)) (show err)
1074-- | Get the next connection ID
1075-- | Get the next connection ID