@@ -72,7 +72,7 @@ import Control.Concurrent.MVar ( MVar
7272 )
7373import Control.Category ((>>>))
7474import Control.Applicative ((<$>))
75−import Control.Monad (forM, forM_, when, unless)
75+import Control.Monad (forM_, when, unless)
7676import Control.Exception (IOException, SomeException, handle, throw, try, bracketOnError)
7777import Data.IORef (IORef, newIORef, writeIORef, readIORef)
7878import Data.ByteString (ByteString)
@@ -82,7 +82,7 @@ import Data.Int (Int32)
8282import Data.IntMap (IntMap)
8383import qualified Data.IntMap as IntMap (empty)
8484import 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)
8686import Data.Map (Map)
8787import qualified Data.Map as Map (empty, elems, size)
8888import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
@@ -279,7 +279,7 @@ data RemoteEndPoint = RemoteEndPoint
279279 }
280280
281281data RemoteState =
282− RemoteEndPointInvalid (FailedWith ConnectErrorCode)
282+ RemoteEndPointInvalid (TransportError ConnectErrorCode)
283283 | RemoteEndPointValid ValidRemoteEndPointState
284284 | RemoteEndPointClosing (MVar ()) ValidRemoteEndPointState
285285 | RemoteEndPointClosed
@@ -335,6 +335,7 @@ createTransport host port = do
335335 where
336336 terminationHandler :: TCPTransport -> SomeException -> IO ()
337337 terminationHandler transport _ = do
338+ -- TODO: we currently don't make a distinction between endpoint failure and manual closure
338339 mTSt <- modifyMVar (transportState transport) $ \st -> case st of
339340 TransportValid vst -> return (TransportClosed, Just vst)
340341 TransportClosed -> return (TransportClosed, Nothing)
@@ -350,7 +351,7 @@ createTransport host port = do
350351--------------------------------------------------------------------------------
351352
352353-- | Create a new endpoint
353−apiNewEndPoint :: TCPTransport -> IO (Either (FailedWith NewEndPointErrorCode) EndPoint)
354+apiNewEndPoint :: TCPTransport -> IO (Either (TransportError NewEndPointErrorCode) EndPoint)
354355apiNewEndPoint transport = try $ do
355356 ourEndPoint <- createLocalEndPoint transport
356357 return EndPoint
@@ -362,14 +363,14 @@ apiNewEndPoint transport = try $ do
362363 , resolveMulticastGroup = return . Left . const resolveMulticastGroupError
363364 }
364365 where
365− newMulticastGroupError = FailedWith NewMulticastGroupUnsupported "TCP does not support multicast"
366− resolveMulticastGroupError = FailedWith ResolveMulticastGroupUnsupported "TCP does not support multicast"
366+ newMulticastGroupError = TransportError NewMulticastGroupUnsupported "TCP does not support multicast"
367+ resolveMulticastGroupError = TransportError ResolveMulticastGroupUnsupported "TCP does not support multicast"
367368
368369-- | Connnect to an endpoint
369370apiConnect :: LocalEndPoint -- ^ Local end point
370371 -> EndPointAddress -- ^ Remote address
371372 -> Reliability -- ^ Reliability (ignored)
372− -> IO (Either (FailedWith ConnectErrorCode) Connection)
373+ -> IO (Either (TransportError ConnectErrorCode) Connection)
373374apiConnect ourEndPoint theirAddress _ | localAddress ourEndPoint == theirAddress =
374375 connectToSelf ourEndPoint
375376apiConnect ourEndPoint theirAddress _ = try $ do
@@ -413,23 +414,23 @@ apiClose theirEndPoint connId connAlive = void . tryIO $ do
413414--
414415-- RELY: The remote endpoint must be in state 'RemoteEndPointValid' or 'RemoteEndPointClosed'
415416-- 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) ())
417418apiSend theirEndPoint connId connAlive payload = do
418419 withMVar (remoteState theirEndPoint) $ \st -> case st of
419420 RemoteEndPointValid vst -> do
420421 alive <- readIORef connAlive
421422 try $ if alive
422423 then mapExceptionIO sendFailed $ sendOn vst (encodeInt32 connId : prependLength payload)
423− else throw $ FailedWith SendConnectionClosed "Connection closed"
424+ else throw $ TransportError SendFailed "Connection closed"
424425 RemoteEndPointClosed -> do
425− return (Left $ FailedWith SendConnectionClosed "Endpoint closed")
426+ return (Left $ TransportError SendFailed "Endpoint closed")
426427 RemoteEndPointClosing _ _ ->
427428 error "apiSend RELY violation"
428429 RemoteEndPointInvalid _ ->
429430 error "apiSend RELY violation"
430431 where
431− sendFailed :: IOException -> FailedWith SendErrorCode
432− sendFailed = FailedWith SendFailed . show
432+ sendFailed :: IOException -> TransportError SendErrorCode
433+ sendFailed = TransportError SendFailed . show
433434
434435-- | Force-close the endpoint
435436apiCloseEndPoint :: TCPTransport -> LocalEndPoint -> IO ()
@@ -448,37 +449,37 @@ apiCloseEndPoint transport ourEndPoint = do
448449 return ()
449450 Just vst -> do
450451 -- 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
452453 forM_ (vst ^. internalThreads) killThread
453454 -- We send a single message to the endpoint that it is closed. Subsequent
454455 -- calls will block. We could change this so that all subsequent calls to
455456 -- receive return an error, but this would mean checking for some state on
456457 -- every call to receive, which is an unnecessary overhead.
457− writeChan (localChannel ourEndPoint) (ErrorEvent $ ErrorEventEndPointClosed incoming)
458+ writeChan (localChannel ourEndPoint) EndPointClosed
458459 where
459460 -- Close the remote socket and return the set of all incoming connections
460− tryCloseRemoteSocket :: RemoteEndPoint -> IO IntSet
461+ tryCloseRemoteSocket :: RemoteEndPoint -> IO ()
461462 tryCloseRemoteSocket theirEndPoint = do
462463 -- We make an attempt to close the connection nicely (by sending a CloseSocket first)
463− modifyMVar (remoteState theirEndPoint) $ \st ->
464+ modifyMVar_ (remoteState theirEndPoint) $ \st ->
464465 case st of
465466 RemoteEndPointInvalid _ ->
466− return (st, IntSet.empty)
467+ return st
467468 RemoteEndPointValid conn -> do
468469 -- Try to send a CloseSocket request
469470 tryIO $ sendOn conn [encodeInt32 CloseSocket]
470471 -- .. but even if it fails, close the socket anyway
471472 -- (hence, two separate calls to tryIO)
472473 tryIO $ N.sClose (remoteSocket conn)
473− return (RemoteEndPointClosed, conn ^. remoteIncoming)
474+ return RemoteEndPointClosed
474475 RemoteEndPointClosing _ conn -> do
475476 tryIO $ N.sClose (remoteSocket conn)
476− return (RemoteEndPointClosed, IntSet.empty)
477+ return RemoteEndPointClosed
477478 RemoteEndPointClosed ->
478− return (RemoteEndPointClosed, IntSet.empty)
479+ return RemoteEndPointClosed
479480
480481-- | 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)
482483connectToSelf ourEndPoint = do
483484 -- Here connAlive must an MVar because it is not protected by another lock
484485 connAlive <- newMVar True
@@ -492,7 +493,7 @@ connectToSelf ourEndPoint = do
492493 ourChan :: Chan Event
493494 ourChan = localChannel ourEndPoint
494495
495− selfSend :: MVar Bool -> ConnectionId -> [ByteString] -> IO (Either (FailedWith SendErrorCode) ())
496+ selfSend :: MVar Bool -> ConnectionId -> [ByteString] -> IO (Either (TransportError SendErrorCode) ())
496497 selfSend connAlive connId msg = do
497498 modifyMVar connAlive $ \alive ->
498499 if alive
@@ -500,7 +501,7 @@ connectToSelf ourEndPoint = do
500501 writeChan ourChan (Received connId msg)
501502 return (alive, Right ())
502503 else
503− return (alive, Left (FailedWith SendConnectionClosed "Connection closed"))
504+ return (alive, Left (TransportError SendFailed "Connection closed"))
504505
505506 selfClose :: MVar Bool -> ConnectionId -> IO ()
506507 selfClose connAlive connId = do
@@ -514,7 +515,7 @@ connectToSelf ourEndPoint = do
514515
515516-- | Create a new local endpoint
516517--
517−-- May throw a FailedWith NewEndPointErrorCode exception if the transport is closed.
518+-- May throw a TransportError NewEndPointErrorCode exception if the transport is closed.
518519createLocalEndPoint :: TCPTransport -> IO LocalEndPoint
519520createLocalEndPoint transport = do
520521 chan <- newChan
@@ -535,7 +536,7 @@ createLocalEndPoint transport = do
535536 }
536537 return (TransportValid . (localEndPointAt addr ^= Just localEndPoint) $ vst, localEndPoint)
537538 TransportClosed ->
538− throw (FailedWith NewEndPointTransportFailure "Transport closed")
539+ throw (TransportError NewEndPointFailed "Transport closed")
539540
540541-- | Request a connection to a remote endpoint
541542--
@@ -543,7 +544,7 @@ createLocalEndPoint transport = do
543544-- the remote endpoint was in 'RemoteEndPointClosing' state then we will
544545-- additionally block until that is resolved.
545546--
546−-- May throw a FailedWith ConnectErrorCode exception.
547+-- May throw a TransportError ConnectErrorCode exception.
547548requestConnectionTo :: LocalEndPoint
548549 -> EndPointAddress
549550 -> IO (RemoteEndPoint, ConnectionId)
@@ -599,7 +600,7 @@ requestConnectionTo ourEndPoint theirAddress = go
599600 modifyMVar_ theirState $ \(RemoteEndPointValid ep) ->
600601 return (RemoteEndPointValid . (remoteOutgoing ^: (+ 1)) $ ep)
601602 -- TODO: should we call closeIfUnused here?
602− throw $ FailedWith ConnectFailed (show err)
603+ throw $ TransportError ConnectFailed (show err)
603604
604605 -- Do the actual connection request. This blocks until the remote
605606 -- endpoint replies (but note that we don't hold any locks at this
@@ -617,7 +618,7 @@ requestConnectionTo ourEndPoint theirAddress = go
617618 findTheirEndPoint = do
618619 (theirEndPoint, isNew) <- modifyMVar ourState $ \st -> case st of
619620 LocalEndPointClosed ->
620− throw (FailedWith ConnectFailed "Local endpoint closed")
621+ throw (TransportError ConnectFailed "Local endpoint closed")
621622 LocalEndPointValid vst ->
622623 case vst ^. localConnectionTo theirAddress of
623624 Just theirEndPoint ->
@@ -674,21 +675,21 @@ setupRemoteEndPoint (ourEndPoint, theirEndPoint) = do
674675 putMVar theirState (RemoteEndPointInvalid err)
675676 where
676677 theirState = remoteState theirEndPoint
677− invalidAddress = FailedWith ConnectInvalidAddress
678+ invalidAddress = TransportError ConnectNotFound
678679
679680-- | Establish a connection to a remote endpoint
680681socketToEndPoint :: EndPointAddress -- ^ Our address
681682 -> EndPointAddress -- ^ Their address
682− -> IO (Either (FailedWith ConnectErrorCode) (N.Socket, ConnectionRequestResponse))
683+ -> IO (Either (TransportError ConnectErrorCode) (N.Socket, ConnectionRequestResponse))
683684socketToEndPoint (EndPointAddress ourAddress) theirAddress = try $ do
684685 (host, port, theirEndPointId) <- case decodeEndPointAddress theirAddress of
685− Nothing -> throw (invalidAddress . userError $ "Could not parse")
686+ Nothing -> throw (failed . userError $ "Could not parse")
686687 Just dec -> return dec
687688 addr:_ <- mapExceptionIO invalidAddress $ N.getAddrInfo Nothing (Just host) (Just port)
688689 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)
689692 response <- mapExceptionIO failed $ do
690− N.setSocketOption sock N.ReuseAddr 1
691− N.connect sock (N.addrAddress addr)
692693 sendMany sock (encodeInt32 theirEndPointId : prependLength [ourAddress])
693694 recvInt32 sock
694695 case tryToEnum response of
@@ -701,10 +702,10 @@ socketToEndPoint (EndPointAddress ourAddress) theirAddress = try $ do
701702 -- putStrLn $ "Created client socket " ++ show sock
702703 return sock
703704
704− invalidAddress, insufficientResources, failed :: IOException -> FailedWith ConnectErrorCode
705− invalidAddress = FailedWith ConnectInvalidAddress . show
706− insufficientResources = FailedWith ConnectInsufficientResources . show
707− failed = FailedWith ConnectFailed . show
705+ invalidAddress, insufficientResources, failed :: IOException -> TransportError ConnectErrorCode
706+ invalidAddress = TransportError ConnectNotFound . show
707+ insufficientResources = TransportError ConnectInsufficientResources . show
708+ failed = TransportError ConnectFailed . show
708709
709710-- | Remove reference to a remote endpoint from a local endpoint
710711--
@@ -1049,7 +1050,7 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
10491050
10501051 -- Deal with a premature exit
10511052 prematureExit :: N.Socket -> IOException -> IO ()
1052− prematureExit sock _ = do
1053+ prematureExit sock err = do
10531054 N.sClose sock
10541055 removeRemoteEndPoint (ourEndPoint, theirEndPoint)
10551056 mUnclosedConnections <- modifyMVar theirState $ \st ->
@@ -1069,7 +1070,7 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
10691070 Nothing ->
10701071 return ()
10711072 Just unclosedConnections -> writeChan ourChannel . ErrorEvent $
1072− ErrorEventConnectionLost (remoteAddress theirEndPoint) (IntSet.elems unclosedConnections)
1073+ TransportError (EventConnectionLost (remoteAddress theirEndPoint) (IntSet.elems unclosedConnections)) (show err)
10731074
10741075-- | Get the next connection ID
10751076--