@@ -58,13 +58,11 @@ import Control.Concurrent.MVar ( MVar
59 , newEmptyMVar
59 , newEmptyMVar
60 , withMVar
60 , withMVar
63import Control.Category ((>>>))
62import Control.Category ((>>>))
64import Control.Applicative ((<*>), (*>), (<$>))
63import Control.Applicative ((<*>), (*>), (<$>))
65−import Control.Monad (forM_, void, when, mzero, unless)
64+import Control.Monad (forM, forM_, void, when, unless)
66import Control.Monad.Error (ErrorT(ErrorT), runErrorT, throwError)
65import Control.Monad.Error (ErrorT(ErrorT), runErrorT, throwError)
67−import Control.Monad.Trans.Maybe (MaybeT, runMaybeT)
68import Control.Monad.IO.Class (liftIO)
66import Control.Monad.IO.Class (liftIO)
69import Control.Exception (IOException, SomeException)
67import Control.Exception (IOException, SomeException)
70import Data.IORef (IORef, newIORef, writeIORef, readIORef)
68import Data.IORef (IORef, newIORef, writeIORef, readIORef)
@@ -75,7 +73,7 @@ import Data.Int (Int32)
75import Data.IntMap (IntMap)
73import Data.IntMap (IntMap)
76import qualified Data.IntMap as IntMap (empty)
74import qualified Data.IntMap as IntMap (empty)
77import Data.IntSet (IntSet)
75import Data.IntSet (IntSet)
78−import qualified Data.IntSet as IntSet (empty, insert, elems, singleton, null, delete)
76+import qualified Data.IntSet as IntSet (empty, insert, elems, singleton, null, delete, unions)
79import Data.Map (Map)
77import Data.Map (Map)
80import qualified Data.Map as Map (empty, elems, size)
78import qualified Data.Map as Map (empty, elems, size)
81import Data.Lens.Lazy (Lens, lens, intMapLens, mapLens, (^.), (^=), (^+=), (^%=))
79import Data.Lens.Lazy (Lens, lens, intMapLens, mapLens, (^.), (^=), (^+=), (^%=))
@@ -396,28 +394,30 @@ apiCloseEndPoint transport ourEndPoint = do
396 -- We take the endpoint state and don't put it back
394 -- We take the endpoint state and don't put it back
397 -- (this may block some threads, but we don't care: we are killing them anyway)
395 -- (this may block some threads, but we don't care: we are killing them anyway)
398 ourState <- takeMVar (localState ourEndPoint)
396 ourState <- takeMVar (localState ourEndPoint)
399− --incoming <- IntSet.elems <$> takeMVar (localIncoming ourEndPoint)
397+ -- Close all endpoints and kill all threads
400− let incoming = undefined
398+ incoming <- IntSet.elems . IntSet.unions <$> (forM (Map.elems $ ourState ^. localConnections) $ tryCloseRemoteSocket)
401− -- Kill all threads associated with this endpoint and close all sockets
402 forM_ (ourState ^. internalThreads) killThread
399 forM_ (ourState ^. internalThreads) killThread
403− forM_ (Map.elems $ ourState ^. localConnections) $ \theirEndPoint ->
404− runMaybeT $ remoteSocketOf theirEndPoint >>= liftIO . N.sClose
405 -- We send a single message to the endpoint that it is closed. Subsequent
400 -- We send a single message to the endpoint that it is closed. Subsequent
406 -- calls will block. We could change this so that all subsequent calls to
401 -- calls will block. We could change this so that all subsequent calls to
407 -- receive return an error, but this would mean checking for some state on
402 -- receive return an error, but this would mean checking for some state on
408 -- every call to receive, which is an unnecessary overhead.
403 -- every call to receive, which is an unnecessary overhead.
409 writeChan (localChannel ourEndPoint) (ErrorEvent $ ErrorEventEndPointClosed incoming)
404 writeChan (localChannel ourEndPoint) (ErrorEvent $ ErrorEventEndPointClosed incoming)
411− remoteSocketOf :: RemoteEndPoint -> MaybeT IO N.Socket
406+ -- Close the remote socket and return the set of all incoming connections
407+ tryCloseRemoteSocket :: RemoteEndPoint -> IO IntSet
412− remoteSocketOf theirEndPoint = do
408+ tryCloseRemoteSocket theirEndPoint = do
413 -- Like above, we take but don't put back
409 -- Like above, we take but don't put back
414− mRemoteState <- liftIO $ tryTakeMVar (remoteState theirEndPoint)
410+ st <- takeMVar (remoteState theirEndPoint)
411+ -- We make an attempt to close the connection nicely (by sending a CloseSocket first)
415− case mRemoteState of
412+ case st of
417− Just (RemoteEndPointInvalid _) -> mzero
413+ RemoteEndPointInvalid _ -> do return IntSet.empty
418− Just (RemoteEndPointValid conn) -> return $ remoteSocket conn
414+ RemoteEndPointValid conn -> do tryIO $ do
415+ sendOn conn [encodeInt32 CloseSocket]
416+ N.sClose (remoteSocket conn)
417+ return (conn ^. remoteIncoming)
419− Just (RemoteEndPointClosing _ conn) -> return $ remoteSocket conn
418+ RemoteEndPointClosing _ conn -> do tryIO $ N.sClose (remoteSocket conn)
420− Just RemoteEndPointClosed -> mzero
420+ RemoteEndPointClosed -> do return IntSet.empty
422-- | Special case of 'apiConnect': connect an endpoint to itself
422-- | Special case of 'apiConnect': connect an endpoint to itself
423connectToSelf :: LocalEndPoint -> IO (Either (FailedWith ConnectErrorCode) Connection)
423connectToSelf :: LocalEndPoint -> IO (Either (FailedWith ConnectErrorCode) Connection)
@@ -906,7 +906,6 @@ handleIncomingMessages (ourEndPoint, theirEndPoint) = do
906 Just unclosedConnections -> writeChan ourChannel . ErrorEvent $
906 Just unclosedConnections -> writeChan ourChannel . ErrorEvent $
907 ErrorEventConnectionLost (remoteAddress theirEndPoint) (IntSet.elems unclosedConnections)
907 ErrorEventConnectionLost (remoteAddress theirEndPoint) (IntSet.elems unclosedConnections)
910-- | Get the next connection ID
909-- | Get the next connection ID
911getNextConnectionId :: LocalEndPoint -> IO ConnectionId
910getNextConnectionId :: LocalEndPoint -> IO ConnectionId
912getNextConnectionId ourEndpoint =
911getNextConnectionId ourEndpoint =