@@ -58,7 +58,7 @@ import Control.Concurrent.MVar ( MVar
5858import Control.Category ((>>>))
5959import Control.Applicative ((<*>), (*>), (<$>))
6060import Control.Monad (forM_, void, unless)
61−import Control.Monad.Error (MonadError, ErrorT(ErrorT), runErrorT, throwError)
61+import Control.Monad.Error (ErrorT(ErrorT), runErrorT)
6262import Control.Monad.IO.Class (liftIO)
6363import Control.Exception (IOException, bracket_)
6464import Data.ByteString (ByteString)
@@ -256,15 +256,16 @@ apiConnect :: LocalEndPoint -- ^ Local end point
256256apiConnect ourEndPoint theirAddress _ = runErrorT $ do
257257 theirEndPoint <- findRemoteEndPoint ourEndPoint theirAddress
258258 connId <- requestNewConnection (ourEndPoint, theirEndPoint)
259− return $ Connection { send = apiSend theirEndPoint connId
260− , close = apiClose (ourEndPoint, theirEndPoint) connId
259+ connAlive <- liftIO $ newMVar True
260+ return $ Connection { send = apiSend theirEndPoint connId connAlive
261+ , close = apiClose (ourEndPoint, theirEndPoint) connId connAlive
261262 }
262263
263264-- | Close a connection
264265--
265266-- TODO: We ignore errors during a close. Is that right?
266−apiClose :: Conn -> ConnectionId -> IO ()
267−apiClose (ourEndPoint, theirEndPoint) connId = void . tryIO $ do
267+apiClose :: Conn -> ConnectionId -> MVar Bool -> IO ()
268+apiClose (ourEndPoint, theirEndPoint) connId connAlive = void . tryIO $ do
268269 sendTo theirEndPoint [encodeInt32 CloseConnection, encodeInt32 connId]
269270 decreaseRemoteRefCount (ourEndPoint, theirEndPoint)
270271
@@ -272,8 +273,8 @@ apiClose (ourEndPoint, theirEndPoint) connId = void . tryIO $ do
272273--
273274-- TODO: we should have some per-connection state so that the user cannot send
274275-- to the connection after they closed it, or close it more than once
275−apiSend :: RemoteEndPoint -> ConnectionId -> [ByteString] -> IO (Either (FailedWith SendErrorCode) ())
276−apiSend theirEndPoint connId payload = runErrorT $
276+apiSend :: RemoteEndPoint -> ConnectionId -> MVar Bool -> [ByteString] -> IO (Either (FailedWith SendErrorCode) ())
277+apiSend theirEndPoint connId connAlive payload = runErrorT $
277278 failWithIO (FailedWith SendFailed . show) $
278279 sendTo theirEndPoint (encodeInt32 connId : prependLength payload)
279280
@@ -372,16 +373,19 @@ connectToRemoteEndPoint ourEndPoint theirMVar theirAddress = do
372373
373374-- | Request a new connection
374375requestNewConnection :: Conn -> ErrorT (FailedWith ConnectErrorCode) IO ConnectionId
375−requestNewConnection (ourEndPoint, theirEndPoint) = do
376− mcid <- failWithIO (failed . show) $ do
376+requestNewConnection (ourEndPoint, theirEndPoint) = ErrorT $ do
377+ -- We increment the refcount before sending the request, because if a
378+ -- CloseSocket request comes this way first we want to ignore it
379+ increaseRemoteRefCount (ourEndPoint, theirEndPoint)
380+ mcid <- tryIO $ do
377381 reply <- sendRemoteRequest (ourEndPoint, theirEndPoint) RequestConnectionId
378382 decodeInt32 . BS.concat <$> takeMVar reply
379− case mcid of
380− Nothing ->
381− throwError (failed "Invalid integer")
382− Just cid -> do
383− liftIO $ increaseRemoteRefCount (ourEndPoint, theirEndPoint)
384− return cid
383+ case mcid of
384+ Left err -> do decreaseRemoteRefCount (ourEndPoint, theirEndPoint)
385+ return . Left $ failed (show err)
386+ Right Nothing -> do decreaseRemoteRefCount (ourEndPoint, theirEndPoint)
387+ return . Left $ failed "Invalid integer"
388+ Right (Just cid) -> do return . Right $ cid
385389 where
386390 failed = FailedWith ConnectFailed
387391