From 7ce2393915366a572379797be933fec13f3b022e Mon Sep 17 00:00:00 2001 From: Alan McGovern Date: Sun, 30 Jun 2024 22:35:36 +0100 Subject: [PATCH] Fix integration test failures under .net 4.7.2 If a socket is disposed after 'ConnectAsync' is invoked but before the native 'connect' begins, you can get an 'unexpected' objectdisposedexceptionw which causes the SocketAsyncEventArgs object to be left in the 'operation is in progress' state forever. This doesn't happen under newer frameworks, so use a fallback implementation for .NET 4.7.2 and netstandard 2.0/2.1. --- .../SocketConnector.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/MonoTorrent.Connections/MonoTorrent.Connections/SocketConnector.cs b/src/MonoTorrent.Connections/MonoTorrent.Connections/SocketConnector.cs index 4259bb50d..f2b4c04de 100644 --- a/src/MonoTorrent.Connections/MonoTorrent.Connections/SocketConnector.cs +++ b/src/MonoTorrent.Connections/MonoTorrent.Connections/SocketConnector.cs @@ -71,6 +71,30 @@ static void HandleOperationCompleted (object? sender, SocketAsyncEventArgs e) } +#if NETSTANDARD2_0 || NETSTANDARD2_1 || NET472 + public async ReusableTask ConnectAsync (Uri uri, CancellationToken token) + { + var socket = new Socket ((uri.Scheme == "ipv4") ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); + var endPoint = new IPEndPoint (IPAddress.Parse (uri.Host), uri.Port); + + using var registration = token.Register (SocketDisposer, socket); + + try { + // `Socket.ConnectAsync (SocketAsyncEventArgs)` cannot be safely used under .NET 4.7.2. + // .NET 4.7.2 has a bug whereby disposing a socket (so it's safehandle is invalid) before the async operation has fully begun + // causes the 'SocketAsyncEventArgs' to be left in an inconsistent state (permanently in the 'operation in progress' state) + // so it cannot be reused. Work around it by using the synchronous implementation. + // + // This issue caused random integration test deadlocks/hangs under .NET 4.7.2 as socket connections couldn't be made. + await new ThreadSwitcher (); + socket.Connect (endPoint); + } catch { + socket.Dispose (); + throw; + } + return socket; + } +#else public async ReusableTask ConnectAsync (Uri uri, CancellationToken token) { var socket = new Socket ((uri.Scheme == "ipv4") ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); @@ -98,5 +122,6 @@ public async ReusableTask ConnectAsync (Uri uri, CancellationToken token } return socket; } +#endif } }