From 52e7103868622a159fbd84cc34a3c0facd59b38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BAlio=20Ribeiro=20dos=20Anjos?= Date: Wed, 10 Jun 2026 17:17:30 -0400 Subject: [PATCH 1/3] fix(System.Net.Sockets): avoid errors for pending nonblocking connects Non-blocking Socket.Connect can return WouldBlock on Windows or InProgress on Unix while the OS connect attempt remains pending. Treat those results as pending telemetry states instead of failures. Leave the emitted connect Activity status unset, omit error.type, and avoid emitting ConnectFailed for these pending results. Add a regression test for the real synchronous Connect path so the pending result is verified through the public socket API. --- .../System/Net/Sockets/SocketsTelemetry.cs | 11 +++++-- .../tests/FunctionalTests/TelemetryTest.cs | 31 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs index 1171961a204351..f2857f632636e4 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs @@ -147,9 +147,16 @@ public void AfterConnect(SocketError error, Activity? activity, string? exceptio long newCount = Interlocked.Decrement(ref _currentOutgoingConnectAttempts); Debug.Assert(newCount >= 0); + // _currentOutgoingConnectAttempts tracks managed Connect calls. A non-blocking Connect call + // can return WouldBlock (Windows) or InProgress (Unix) while the OS connect attempt remains + // pending after this method decrements that counter; its eventual outcome is not observable + // here. Don't report these pending results as failures. + // See https://github.com/dotnet/runtime/issues/129252 + bool connectPending = error is SocketError.WouldBlock or SocketError.InProgress; + if (activity is not null) { - if (error != SocketError.Success) + if (error != SocketError.Success && !connectPending) { activity.SetStatus(ActivityStatusCode.Error); activity.SetTag("error.type", GetErrorType(error)); @@ -163,7 +170,7 @@ public void AfterConnect(SocketError error, Activity? activity, string? exceptio Debug.Assert(exceptionMessage is null); Interlocked.Increment(ref _outgoingConnectionsEstablished); } - else + else if (!connectPending) { ConnectFailed(error, exceptionMessage); } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs index 69f61fc180a49c..94bc43e74abb40 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/TelemetryTest.cs @@ -158,6 +158,37 @@ static void VerifyTcpConnectActivity(Activity activity, IPEndPoint remoteEndPoin ActivityAssert.HasTag(activity, "network.transport", "tcp"); } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + public async Task Connect_NonBlockingPending_ActivityNotMarkedAsError() + { + await RemoteExecutor.Invoke(static () => + { + using Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + server.BindToAnonymousPort(IPAddress.Loopback); + server.Listen(); + + using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) + { + Blocking = false + }; + + using ActivityRecorder recorder = new ActivityRecorder(ActivitySourceName, ActivityName); + + // A non-blocking connect reports WouldBlock (Windows) or InProgress (Unix) by design while + // the attempt continues in the background. The "socket connect" activity is started and + // stopped synchronously inside Connect, so its final state can be asserted immediately. + SocketException ex = Assert.Throws(() => client.Connect(server.LocalEndPoint)); + Assert.True(ex.SocketErrorCode is SocketError.WouldBlock or SocketError.InProgress, + $"Unexpected SocketError: {ex.SocketErrorCode}"); + + recorder.VerifyActivityRecorded(1); + Activity activity = recorder.LastFinishedActivity; + VerifyTcpConnectActivity(activity, (IPEndPoint)server.LocalEndPoint, ipv6: false); + Assert.Equal(ActivityStatusCode.Unset, activity.Status); + Assert.Null(activity.GetTagItem("error.type")); + }).DisposeAsync(); + } + [OuterLoop("Connection failure takes long on Windows.")] [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [MemberData(nameof(SocketMethods_WithBools_MemberData))] From 4dfcd1cef6260a5fc81a0afa57d7b2bab47b900d Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Mon, 15 Jun 2026 20:17:06 +0200 Subject: [PATCH 2/3] Remove reference to specific issue --- .../src/System/Net/Sockets/SocketsTelemetry.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs index f2857f632636e4..79ad8d73a5cb11 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs @@ -151,7 +151,6 @@ public void AfterConnect(SocketError error, Activity? activity, string? exceptio // can return WouldBlock (Windows) or InProgress (Unix) while the OS connect attempt remains // pending after this method decrements that counter; its eventual outcome is not observable // here. Don't report these pending results as failures. - // See https://github.com/dotnet/runtime/issues/129252 bool connectPending = error is SocketError.WouldBlock or SocketError.InProgress; if (activity is not null) From 935cf1beb76d8ccea9ba1b8b0ead3d5bba3a89ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=BAlio=20Ribeiro=20dos=20Anjos?= Date: Thu, 18 Jun 2026 12:30:39 -0400 Subject: [PATCH 3/3] docs(System.Net.Sockets): clarify pending connect telemetry comment --- .../src/System/Net/Sockets/SocketsTelemetry.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs index 79ad8d73a5cb11..93baeb37b0a5f2 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketsTelemetry.cs @@ -149,8 +149,9 @@ public void AfterConnect(SocketError error, Activity? activity, string? exceptio // _currentOutgoingConnectAttempts tracks managed Connect calls. A non-blocking Connect call // can return WouldBlock (Windows) or InProgress (Unix) while the OS connect attempt remains - // pending after this method decrements that counter; its eventual outcome is not observable - // here. Don't report these pending results as failures. + // pending after this method returns. That later result may be observed by Socket when it + // checks whether the pending non-blocking connect completed, but it is not available to this + // synchronous Connect telemetry callback. Don't report these pending results as failures. bool connectPending = error is SocketError.WouldBlock or SocketError.InProgress; if (activity is not null)