@@ -10,9 +10,6 @@
1010-- Applications that use the TCP transport should use
1111-- 'Network.Socket.withSocketsDo' in their main function for Windows
1212-- compatibility (see "Network.Socket").
13−--
14−-- TODOs:
15−-- * Output exception on channel after endpoint is closed
1613module Network.Transport.TCP ( -- * Main API
1714 createTransport
1815 -- * Internals (exposed for unit tests)
@@ -80,6 +77,7 @@ import Control.Monad.Error (ErrorT(..), runErrorT)
8077import Control.Exception ( IOException
8178 , SomeException
8279 , handle
80+ , throw
8381 , throwIO
8482 , try
8583 , bracketOnError
@@ -293,7 +291,7 @@ data ValidLocalEndPointState = ValidLocalEndPointState
293291--
294292-- INV-CLOSE: Whenever we put an endpoint in closed state we remove that
295293-- endpoint from localConnections first, so that if a concurrent thread reads
296−-- the mvar, finds EndPointClosed, and then looks up the endpoint in
294+-- the mvar, finds RemoteEndPointClosed, and then looks up the endpoint in
297295-- localConnections it is guaranteed to either find a different remote
298296-- endpoint, or else none at all.
299297-- INV-RESOLVE: Whenever we move a endpoint from Closing to Closed state, we
@@ -399,7 +397,9 @@ createTransportExposeInternals host port = do
399397 return
400398 ( Transport
401399 { newEndPoint = apiNewEndPoint transport
402− , closeTransport = let evs = [EndPointClosed] in
400+ , closeTransport = let evs = [ EndPointClosed
401+ , throw $ userError "Transport closed"
402+ ] in
403403 apiCloseTransport transport (Just tid) evs
404404 }
405405 , TransportInternals
@@ -410,8 +410,10 @@ createTransportExposeInternals host port = do
410410 where
411411 terminationHandler :: TCPTransport -> SomeException -> IO ()
412412 terminationHandler transport ex = do
413− let ev = ErrorEvent (TransportError EventTransportFailed (show ex))
414− apiCloseTransport transport Nothing [ev]
413+ let evs = [ ErrorEvent (TransportError EventTransportFailed (show ex))
414+ , throw $ userError "Transport closed"
415+ ]
416+ apiCloseTransport transport Nothing evs
415417
416418--------------------------------------------------------------------------------
417419-- API functions --
@@ -438,7 +440,10 @@ apiNewEndPoint transport = try $ do
438440 { receive = readChan (localChannel ourEndPoint)
439441 , address = localAddress ourEndPoint
440442 , connect = apiConnect ourEndPoint
441− , closeEndPoint = apiCloseEndPoint transport [EndPointClosed] ourEndPoint
443+ , closeEndPoint = let evs = [ EndPointClosed
444+ , throw $ userError "Endpoint closed"
445+ ] in
446+ apiCloseEndPoint transport evs ourEndPoint
442447 , newMulticastGroup = return . Left $ newMulticastGroupError
443448 , resolveMulticastGroup = return . Left . const resolveMulticastGroupError
444449 }
@@ -501,7 +506,7 @@ apiSend :: EndPointPair -- ^ Local and remote endpoint
501506 -> [ByteString] -- ^ Payload
502507 -> IO (Either (TransportError SendErrorCode) ())
503508apiSend (ourEndPoint, theirEndPoint) connId connAlive payload =
504− -- The 'join' joins the inner exception that we explicitly return) for
509+ -- The 'join' joins the inner exception (which we explicitly return, for
505510 -- instance if the connection is closed) with the outer exception (which is
506511 -- returned by 'try' when an exception is thrown by 'sendOn', and handled
507512 -- by 'withRemoteState')
@@ -733,7 +738,7 @@ requestConnectionTo ourEndPoint theirAddress = go
733738 readMVar resolved >> go
734739
735740 RemoteEndPointClosed ->
736− -- EndPointClosed indicates that a concurrent thread was in the
741+ -- RemoteEndPointClosed indicates that a concurrent thread was in the
737742 -- process of closing the TCP connection to the remote endpoint when
738743 -- we obtained a reference to it. By INV-CLOSE we can assume that the
739744 -- remote endpoint will now have been removed from ourState, so we