@@ -74,7 +74,7 @@ import Control.Concurrent.MVar ( MVar
7474import Control.Category ((>>>))
7575import Control.Applicative ((<$>))
7676import Control.Monad (forM_, when, unless)
77−import Control.Exception (IOException, SomeException, handle, throw, try, bracketOnError)
77+import Control.Exception (IOException, SomeException, handle, throw, try, bracketOnError, mask_)
7878import Data.IORef (IORef, newIORef, writeIORef, readIORef)
7979import Data.ByteString (ByteString)
8080import qualified Data.ByteString as BS (concat)
@@ -628,8 +628,9 @@ requestConnectionTo ourEndPoint theirAddress = go
628628 RemoteEndPointClosed -> do
629629 -- EndPointClosed indicates that a concurrent thread was in the
630630 -- process of closing the TCP connection to the remote endpoint when
631− -- we obtained a reference to it. The remote endpoint will now have
632− -- been removed from ourState, so we simply try again.
631+ -- we obtained a reference to it. By INV-CLOSE we can assume that the
632+ -- remote endpoint will now have been removed from ourState, so we
633+ -- simply try again.
633634 go
634635
635636 RemoteEndPointValid _ -> do
@@ -641,7 +642,6 @@ requestConnectionTo ourEndPoint theirAddress = go
641642 failureHandler err = do
642643 modifyMVar_ theirState $ \(RemoteEndPointValid ep) ->
643644 return (RemoteEndPointValid . (remoteOutgoing ^: (\x -> x - 1)) $ ep)
644− -- TODO: should we call closeIfUnused here?
645645 throw $ TransportError ConnectFailed (show err)
646646
647647 -- Do the actual connection request. This blocks until the remote
@@ -827,12 +827,23 @@ closeIfUnused theirEndPoint = modifyMVar_ (remoteState theirEndPoint) $ \st ->
827827-- spawned). Returns whether or not a thread was spawned.
828828forkEndPointThread :: LocalEndPoint -> IO () -> IO Bool
829829forkEndPointThread ourEndPoint p =
830− modifyMVar ourState $ \st -> case st of
831− LocalEndPointValid vst -> do
832− tid <- forkIO (p >> removeThread)
833− return (LocalEndPointValid . (internalThreads ^: (tid :)) $ vst, True)
834− LocalEndPointClosed ->
835− return (LocalEndPointClosed, False)
830+ -- We use an explicit mask_ because we don't want to be interrupted until
831+ -- we have registered the thread. In particular, modifyMVar is not good
832+ -- enough because if we get an asynchronous exception after the fork but
833+ -- before the argument to modifyMVar returns we don't want to simply put
834+ -- the old value of the mvar back.
835+ mask_ $ do
836+ st <- takeMVar ourState
837+ case st of
838+ LocalEndPointValid vst -> do
839+ threadRegistered <- newEmptyMVar
840+ tid <- forkIO (takeMVar threadRegistered >> p >> removeThread)
841+ putMVar ourState $ LocalEndPointValid . (internalThreads ^: (tid :)) $ vst
842+ putMVar threadRegistered ()
843+ return True
844+ LocalEndPointClosed -> do
845+ putMVar ourState st
846+ return False
836847 where
837848 removeThread :: IO ()
838849 removeThread = do