From 59025152f097ad5f34e063c377bb8cd8d30719ff Mon Sep 17 00:00:00 2001 From: Geoff Kizer Date: Mon, 26 Apr 2021 12:17:44 -0700 Subject: [PATCH] refactor old APM [Begin/End]Accept methods on top of Task APIs, and enable for Unix as well (#51212) * refactor old APM [Begin/End]Accept methods on top of Task APIs, and enable for Unix as well * fix BeginAccept to throw synchronously, and relevant test changes * fix AcceptReceive test to run on unix too * dispose accepted socket if receive throws * remove CallbackClosure cache * fix test helper to indicate AcceptReceive with EAP is still only supported on Windows Co-authored-by: Geoffrey Kizer --- .../src/System.Net.Sockets.csproj | 3 - .../AcceptOverlappedAsyncResult.Unix.cs | 54 ----- .../AcceptOverlappedAsyncResult.Windows.cs | 134 ----------- .../Sockets/AcceptOverlappedAsyncResult.cs | 34 --- .../src/System/Net/Sockets/Socket.Unix.cs | 20 -- .../src/System/Net/Sockets/Socket.Windows.cs | 27 +-- .../src/System/Net/Sockets/Socket.cs | 215 ++++-------------- .../src/System/Net/Sockets/SocketPal.Unix.cs | 17 -- .../System/Net/Sockets/SocketPal.Windows.cs | 36 +-- .../tests/FunctionalTests/Accept.cs | 43 +--- .../tests/FunctionalTests/DisconnectTest.cs | 21 -- .../tests/FunctionalTests/SocketTestHelper.cs | 7 +- 12 files changed, 55 insertions(+), 556 deletions(-) delete mode 100644 src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.Unix.cs delete mode 100644 src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.Windows.cs delete mode 100644 src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.cs diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index 42314da6c7e9d..c576e1be3c000 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -43,7 +43,6 @@ - @@ -89,7 +88,6 @@ - @@ -184,7 +182,6 @@ Link="Common\System\Net\CompletionPortHelper.Windows.cs" /> - diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.Unix.cs deleted file mode 100644 index 657544b27c319..0000000000000 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.Unix.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics; -using System.Net; -using System.Runtime.InteropServices; -using System.Threading; -using Microsoft.Win32; - -namespace System.Net.Sockets -{ - // AcceptOverlappedAsyncResult - used to take care of storage for async Socket BeginAccept call. - internal sealed partial class AcceptOverlappedAsyncResult : BaseOverlappedAsyncResult - { - private Socket? _acceptedSocket; - - internal Socket? AcceptSocket - { - set - { - // *nix does not support the reuse of an existing socket as the accepted - // socket. - Debug.Assert(value == null, $"Unexpected value: {value}"); - } - } - - public void CompletionCallback(IntPtr acceptedFileDescriptor, byte[] socketAddress, int socketAddressLen, SocketError errorCode) - { - _buffer = null; - _numBytes = 0; - - if (errorCode == SocketError.Success) - { - Debug.Assert(_listenSocket._rightEndPoint != null); - - Internals.SocketAddress remoteSocketAddress = IPEndPointExtensions.Serialize(_listenSocket._rightEndPoint); - System.Buffer.BlockCopy(socketAddress, 0, remoteSocketAddress.Buffer, 0, socketAddressLen); - - _acceptedSocket = _listenSocket.CreateAcceptSocket( - SocketPal.CreateSocket(acceptedFileDescriptor), - _listenSocket._rightEndPoint.Create(remoteSocketAddress)); - } - - base.CompletionCallback(0, errorCode); - } - - internal override object? PostCompletion(int numBytes) - { - _numBytes = numBytes; - return (SocketError)ErrorCode == SocketError.Success ? _acceptedSocket : null; - } - } -} diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.Windows.cs deleted file mode 100644 index d85b2bfc8c4af..0000000000000 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.Windows.cs +++ /dev/null @@ -1,134 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace System.Net.Sockets -{ - // AcceptOverlappedAsyncResult - used to take care of storage for async Socket BeginAccept call. - internal sealed partial class AcceptOverlappedAsyncResult : BaseOverlappedAsyncResult - { - private Socket? _acceptSocket; - private int _addressBufferLength; - - // This method will be called by us when the IO completes synchronously and - // by the ThreadPool when the IO completes asynchronously. (only called on WinNT) - internal override object? PostCompletion(int numBytes) - { - SocketError errorCode = (SocketError)ErrorCode; - - Internals.SocketAddress? remoteSocketAddress = null; - if (errorCode == SocketError.Success) - { - _numBytes = numBytes; - if (NetEventSource.Log.IsEnabled()) LogBuffer(numBytes); - - // get the endpoint - remoteSocketAddress = IPEndPointExtensions.Serialize(_listenSocket._rightEndPoint!); - - IntPtr localAddr; - int localAddrLength; - IntPtr remoteAddr; - - // set the socket context - bool refAdded = false; - SafeHandle safeHandle = _listenSocket.SafeHandle; - try - { - safeHandle.DangerousAddRef(ref refAdded); - IntPtr handle = safeHandle.DangerousGetHandle(); - - Debug.Assert(_buffer != null); - _listenSocket.GetAcceptExSockaddrs( - Marshal.UnsafeAddrOfPinnedArrayElement(_buffer, 0), - _buffer.Length - (_addressBufferLength * 2), - _addressBufferLength, - _addressBufferLength, - out localAddr, - out localAddrLength, - out remoteAddr, - out remoteSocketAddress.InternalSize); - - Marshal.Copy(remoteAddr, remoteSocketAddress.Buffer, 0, remoteSocketAddress.Size); - - errorCode = Interop.Winsock.setsockopt( - _acceptSocket!.SafeHandle, - SocketOptionLevel.Socket, - SocketOptionName.UpdateAcceptContext, - ref handle, - IntPtr.Size); - - if (errorCode == SocketError.SocketError) - { - errorCode = SocketPal.GetLastSocketError(); - } - - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"setsockopt handle:{handle}, AcceptSocket:{_acceptSocket}, returns:{errorCode}"); - } - catch (ObjectDisposedException) - { - errorCode = SocketError.OperationAborted; - } - finally - { - if (refAdded) - { - safeHandle.DangerousRelease(); - } - } - - ErrorCode = (int)errorCode; - } - - if (errorCode != SocketError.Success) - { - return null; - } - - return _listenSocket.UpdateAcceptSocket(_acceptSocket!, _listenSocket._rightEndPoint!.Create(remoteSocketAddress!)); - } - - // SetUnmanagedStructures - // - // This method fills in overlapped structures used in an asynchronous - // overlapped Winsock call. These calls are outside the runtime and are - // unmanaged code, so we need to prepare specific structures and ints that - // lie in unmanaged memory since the overlapped calls may complete asynchronously. - internal void SetUnmanagedStructures(byte[] buffer, int addressBufferLength) - { - // has to be called first to pin memory - base.SetUnmanagedStructures(buffer); - - // Fill in Buffer Array structure that will be used for our send/recv Buffer - _addressBufferLength = addressBufferLength; - _buffer = buffer; - } - - private void LogBuffer(long size) - { - // This should only be called if tracing is enabled. However, there is the potential for a race - // condition where tracing is disabled between a calling check and here, in which case the assert - // may fire erroneously. - Debug.Assert(NetEventSource.Log.IsEnabled()); - Debug.Assert(_buffer != null); - - if (size > -1) - { - NetEventSource.DumpBuffer(this, _buffer, 0, Math.Min((int)size, _buffer.Length)); - } - else - { - NetEventSource.DumpBuffer(this, _buffer); - } - } - - internal Socket AcceptSocket - { - set - { - _acceptSocket = value; - } - } - } -} diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.cs deleted file mode 100644 index 5e6609fedffa0..0000000000000 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/AcceptOverlappedAsyncResult.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Net.Sockets -{ - // AcceptOverlappedAsyncResult - used to take care of storage for async Socket BeginAccept call. - internal sealed partial class AcceptOverlappedAsyncResult : BaseOverlappedAsyncResult - { - private readonly Socket _listenSocket; - private byte[]? _buffer; - - internal AcceptOverlappedAsyncResult(Socket listenSocket, object? asyncState, AsyncCallback? asyncCallback) : - base(listenSocket, asyncState, asyncCallback) - { - _listenSocket = listenSocket; - } - - internal byte[]? Buffer - { - get - { - return _buffer; - } - } - - internal int BytesTransferred - { - get - { - return _numBytes; - } - } - } -} diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs index 79ba1e95c1d3f..a46a30555fd43 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Unix.cs @@ -27,26 +27,6 @@ public SocketInformation DuplicateAndClose(int targetProcessId) throw new PlatformNotSupportedException(SR.net_sockets_duplicateandclose_notsupported); } - public IAsyncResult BeginAccept(int receiveSize, AsyncCallback? callback, object? state) - { - throw new PlatformNotSupportedException(SR.net_sockets_accept_receive_apm_notsupported); - } - - public IAsyncResult BeginAccept(Socket? acceptSocket, int receiveSize, AsyncCallback? callback, object? state) - { - throw new PlatformNotSupportedException(SR.net_sockets_accept_receive_apm_notsupported); - } - - public Socket EndAccept(out byte[] buffer, IAsyncResult asyncResult) - { - throw new PlatformNotSupportedException(SR.net_sockets_accept_receive_apm_notsupported); - } - - public Socket EndAccept(out byte[] buffer, out int bytesTransferred, IAsyncResult asyncResult) - { - throw new PlatformNotSupportedException(SR.net_sockets_accept_receive_apm_notsupported); - } - internal bool PreferInlineCompletions { get => _handle.PreferInlineCompletions; diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs index 11325495e0a31..417386efaddcd 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs @@ -7,6 +7,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Threading; +using System.Threading.Tasks; using System.Runtime.Versioning; namespace System.Net.Sockets @@ -145,30 +146,6 @@ public SocketInformation DuplicateAndClose(int targetProcessId) return info; } - public IAsyncResult BeginAccept(int receiveSize, AsyncCallback? callback, object? state) - { - return BeginAccept(acceptSocket: null, receiveSize, callback, state); - } - - // This is the truly async version that uses AcceptEx. - public IAsyncResult BeginAccept(Socket? acceptSocket, int receiveSize, AsyncCallback? callback, object? state) - { - return BeginAcceptCommon(acceptSocket, receiveSize, callback, state); - } - - public Socket EndAccept(out byte[] buffer, IAsyncResult asyncResult) - { - Socket socket = EndAccept(out byte[] innerBuffer, out int bytesTransferred, asyncResult); - buffer = new byte[bytesTransferred]; - Buffer.BlockCopy(innerBuffer, 0, buffer, 0, bytesTransferred); - return socket; - } - - public Socket EndAccept(out byte[] buffer, out int bytesTransferred, IAsyncResult asyncResult) - { - return EndAcceptCommon(out buffer!, out bytesTransferred, asyncResult); - } - private DynamicWinsockMethods GetDynamicWinsockMethods() { return _dynamicWinsockMethods ??= DynamicWinsockMethods.GetMethods(_addressFamily, _socketType, _protocolType); @@ -439,7 +416,7 @@ private IAsyncResult BeginSendFileInternal(string? fileName, byte[]? preBuffer, throw new SocketException((int)errorCode); } - asyncResult.FinishPostingAsyncOp(ref Caches.SendClosureCache); + asyncResult.FinishPostingAsyncOp(); return asyncResult; } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs index d39abf6609ba5..796bd6e3fa8f7 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs @@ -57,16 +57,6 @@ public partial class Socket : IDisposable private SocketType _socketType; private ProtocolType _protocolType; - // These caches are one degree off of Socket since they're not used in the sync case/when disabled in config. - private CacheSet? _caches; - - private sealed class CacheSet - { - internal CallbackClosure? AcceptClosureCache; - internal CallbackClosure? SendClosureCache; - internal CallbackClosure? ReceiveClosureCache; - } - // Bool marked true if the native socket option IP_PKTINFO or IPV6_PKTINFO has been set. private bool _receivingPacketInformation; @@ -2124,7 +2114,7 @@ public void EndConnect(IAsyncResult asyncResult) } public IAsyncResult BeginDisconnect(bool reuseSocket, AsyncCallback? callback, object? state) => - TaskToApmBeginWithSyncExceptions(DisconnectAsync(reuseSocket).AsTask(), callback, state); + TaskToApm.Begin(DisconnectAsync(reuseSocket).AsTask(), callback, state); public void Disconnect(bool reuseSocket) { @@ -2421,166 +2411,72 @@ public int EndReceiveFrom(IAsyncResult asyncResult, ref EndPoint endPoint) return result.ReceivedBytes; } - // Routine Description: - // - // BeginAccept - Does an async winsock accept, creating a new socket on success - // - // Works by creating a pending accept request the first time, - // and subsequent calls are queued so that when the first accept completes, - // the next accept can be resubmitted in the callback. - // this routine may go pending at which time, - // but any case the callback Delegate will be called upon completion - // - // Arguments: - // - // Callback - Async Callback Delegate that is called upon Async Completion - // State - State used to track callback, set by caller, not required - // - // Return Value: - // - // IAsyncResult - Async result used to retrieve resultant new socket - public IAsyncResult BeginAccept(AsyncCallback? callback, object? state) - { - if (!_isDisconnected) - { - return BeginAcceptCommon(acceptSocket: null, receiveSize: 0, callback, state); - } - - Debug.Assert(Disposed); - ThrowObjectDisposedException(); - return null; // unreachable - } + public IAsyncResult BeginAccept(AsyncCallback? callback, object? state) => + TaskToApm.Begin(AcceptAsync(), callback, state); - private IAsyncResult BeginAcceptCommon(Socket? acceptSocket, int receiveSize, AsyncCallback? callback, object? state) + public Socket EndAccept(IAsyncResult asyncResult) { ThrowIfDisposed(); + return TaskToApm.End(asyncResult); + } + // This method provides support for legacy BeginAccept methods that take a "receiveSize" argument and + // allow data to be received as part of the accept operation. + // There's no direct equivalent of this in the Task APIs, so we mimic it here. + private async Task<(Socket s, byte[] buffer, int bytesReceived)> AcceptAndReceiveHelperAsync(Socket? acceptSocket, int receiveSize) + { // Validate input parameters. if (receiveSize < 0) { throw new ArgumentOutOfRangeException(nameof(receiveSize)); } - // Set up the async result with flowing. - AcceptOverlappedAsyncResult asyncResult = new AcceptOverlappedAsyncResult(this, state, callback); - asyncResult.StartPostingAsyncOp(false); - - // Start the accept. - if (_rightEndPoint == null) - { - throw new InvalidOperationException(SR.net_sockets_mustbind); - } + Socket s = await AcceptAsync(acceptSocket).ConfigureAwait(false); - if (!_isListening) + byte[] buffer; + int bytesReceived; + if (receiveSize == 0) { - throw new InvalidOperationException(SR.net_sockets_mustlisten); + buffer = Array.Empty(); + bytesReceived = 0; } - - SafeSocketHandle? acceptHandle; - asyncResult.AcceptSocket = GetOrCreateAcceptSocket(acceptSocket, false, nameof(acceptSocket), out acceptHandle); - - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"AcceptSocket:{acceptSocket}"); - if (SocketsTelemetry.Log.IsEnabled()) SocketsTelemetry.Log.AcceptStart(_rightEndPoint); - - int socketAddressSize = GetAddressSize(_rightEndPoint); - SocketError errorCode; - try - { - errorCode = SocketPal.AcceptAsync(this, _handle, acceptHandle, receiveSize, socketAddressSize, asyncResult); - } - catch (Exception ex) + else { - if (SocketsTelemetry.Log.IsEnabled()) + buffer = new byte[receiveSize]; + try { - SocketsTelemetry.Log.AfterAccept(SocketError.Interrupted, ex.Message); + bytesReceived = await s.ReceiveAsync(buffer, SocketFlags.None).ConfigureAwait(false); + } + catch + { + s.Dispose(); + throw; } - - throw; } - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"AcceptAsync returns:{errorCode} {asyncResult}"); - - // Throw an appropriate SocketException if the native call fails synchronously. - if (!CheckErrorAndUpdateStatus(errorCode)) - { - UpdateAcceptSocketErrorForDisposed(ref errorCode); - - if (SocketsTelemetry.Log.IsEnabled()) SocketsTelemetry.Log.AfterAccept(errorCode); - - throw new SocketException((int)errorCode); - } + return (s, buffer, bytesReceived); + } - // Finish the flow capture, maybe complete here. - asyncResult.FinishPostingAsyncOp(ref Caches.AcceptClosureCache); + public IAsyncResult BeginAccept(int receiveSize, AsyncCallback? callback, object? state) => + BeginAccept(acceptSocket: null, receiveSize, callback, state); - return asyncResult; - } + public IAsyncResult BeginAccept(Socket? acceptSocket, int receiveSize, AsyncCallback? callback, object? state) => + TaskToApm.Begin(AcceptAndReceiveHelperAsync(acceptSocket, receiveSize), callback, state); - // Routine Description: - // - // EndAccept - Called by user code after I/O is done or the user wants to wait. - // until Async completion, so it provides End handling for async Accept calls, - // and retrieves new Socket object - // - // Arguments: - // - // AsyncResult - the AsyncResult Returned from BeginAccept call - // - // Return Value: - // - // Socket - a valid socket if successful - public Socket EndAccept(IAsyncResult asyncResult) + public Socket EndAccept(out byte[] buffer, IAsyncResult asyncResult) { - return EndAcceptCommon(out _, out _, asyncResult); + Socket socket = EndAccept(out byte[] innerBuffer, out int bytesTransferred, asyncResult); + buffer = new byte[bytesTransferred]; + Buffer.BlockCopy(innerBuffer, 0, buffer, 0, bytesTransferred); + return socket; } - private Socket EndAcceptCommon(out byte[]? buffer, out int bytesTransferred, IAsyncResult asyncResult) - { - if (Disposed) - { - if (SocketsTelemetry.Log.IsEnabled()) SocketsTelemetry.Log.AfterAccept(SocketError.Interrupted); - - ThrowObjectDisposedException(); - } - - // Validate input parameters. - if (asyncResult == null) - { - throw new ArgumentNullException(nameof(asyncResult)); - } - AcceptOverlappedAsyncResult? castedAsyncResult = asyncResult as AcceptOverlappedAsyncResult; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException(SR.net_io_invalidasyncresult, nameof(asyncResult)); - } - if (castedAsyncResult.EndCalled) - { - throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndAccept")); - } - - Socket socket = (Socket)castedAsyncResult.InternalWaitForCompletion()!; - bytesTransferred = (int)castedAsyncResult.BytesTransferred; - buffer = castedAsyncResult.Buffer; - - if (SocketsTelemetry.Log.IsEnabled()) SocketsTelemetry.Log.BytesReceived(bytesTransferred); - castedAsyncResult.EndCalled = true; - - // Throw an appropriate SocketException if the native call failed asynchronously. - SocketError errorCode = (SocketError)castedAsyncResult.ErrorCode; - - if (errorCode != SocketError.Success) - { - UpdateAcceptSocketErrorForDisposed(ref errorCode); - - if (SocketsTelemetry.Log.IsEnabled()) SocketsTelemetry.Log.AfterAccept(errorCode); - - UpdateStatusAfterSocketErrorAndThrowException(errorCode); - } - - if (SocketsTelemetry.Log.IsEnabled()) SocketsTelemetry.Log.AfterAccept(SocketError.Success); - - if (NetEventSource.Log.IsEnabled()) NetEventSource.Accepted(socket, socket.RemoteEndPoint, socket.LocalEndPoint); - return socket; + public Socket EndAccept(out byte[] buffer, out int bytesTransferred, IAsyncResult asyncResult) + { + ThrowIfDisposed(); + Socket s; + (s, buffer, bytesTransferred) = TaskToApm.End<(Socket, byte[], int)>(asyncResult); + return s; } // Disables sends and receives on a socket. @@ -3102,19 +2998,6 @@ private bool SendToAsync(SocketAsyncEventArgs e, CancellationToken cancellationT // Internal and private properties // - private CacheSet Caches - { - get - { - if (_caches == null) - { - // It's not too bad if extra of these are created and lost. - _caches = new CacheSet(); - } - return _caches; - } - } - internal bool Disposed => _disposed != 0; // @@ -3831,17 +3714,5 @@ private static SocketError GetSocketErrorFromFaultedTask(Task t) _ => SocketError.SocketError }; } - - // Helper to maintain existing behavior of Socket APM methods to throw synchronously from Begin*. - private static IAsyncResult TaskToApmBeginWithSyncExceptions(Task task, AsyncCallback? callback, object? state) - { - if (task.IsFaulted) - { - task.GetAwaiter().GetResult(); - Debug.Fail("Task faulted but GetResult did not throw???"); - } - - return TaskToApm.Begin(task, callback, state); - } } } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs index 411f7730e6b91..46c9ad4fc24b5 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs @@ -1968,23 +1968,6 @@ public static async void SendPacketsAsync( } } - public static SocketError AcceptAsync(Socket socket, SafeSocketHandle handle, SafeSocketHandle? acceptHandle, int receiveSize, int socketAddressSize, AcceptOverlappedAsyncResult asyncResult) - { - Debug.Assert(acceptHandle == null, $"Unexpected acceptHandle: {acceptHandle}"); - Debug.Assert(receiveSize == 0, $"Unexpected receiveSize: {receiveSize}"); - - byte[] socketAddressBuffer = new byte[socketAddressSize]; - - IntPtr acceptedFd; - SocketError socketError = handle.AsyncContext.AcceptAsync(socketAddressBuffer, ref socketAddressSize, out acceptedFd, asyncResult.CompletionCallback); - if (socketError == SocketError.Success) - { - asyncResult.CompletionCallback(acceptedFd, socketAddressBuffer, socketAddressSize, SocketError.Success); - } - - return socketError; - } - internal static SocketError Disconnect(Socket socket, SafeSocketHandle handle, bool reuseSocket) { handle.SetToDisconnected(); diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs index e902d22c049bc..8d109203f72fe 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs @@ -1,15 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Win32.SafeHandles; using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; +using Microsoft.Win32.SafeHandles; #if BIGENDIAN using System.Buffers.Binary; @@ -1099,39 +1098,6 @@ public static unsafe SocketError SendFileAsync(SafeSocketHandle handle, FileStre } } - public static unsafe SocketError AcceptAsync(Socket socket, SafeSocketHandle handle, SafeSocketHandle acceptHandle, int receiveSize, int socketAddressSize, AcceptOverlappedAsyncResult asyncResult) - { - // The buffer needs to contain the requested data plus room for two sockaddrs and 16 bytes - // of associated data for each. - int addressBufferSize = socketAddressSize + 16; - byte[] buffer = new byte[receiveSize + ((addressBufferSize) * 2)]; - - // Set up asyncResult for overlapped AcceptEx. - // This call will use completion ports on WinNT. - asyncResult.SetUnmanagedStructures(buffer, addressBufferSize); - try - { - // This can throw ObjectDisposedException. - int bytesTransferred; - bool success = socket.AcceptEx( - handle, - acceptHandle, - Marshal.UnsafeAddrOfPinnedArrayElement(asyncResult.Buffer!, 0), - receiveSize, - addressBufferSize, - addressBufferSize, - out bytesTransferred, - asyncResult.DangerousOverlappedPointer); // SafeHandle was just created in SetUnmanagedStructures - - return asyncResult.ProcessOverlappedResult(success, 0); - } - catch - { - asyncResult.ReleaseUnmanagedStructures(); - throw; - } - } - public static void CheckDualModeReceiveSupport(Socket socket) { // Dual-mode sockets support received packet info on Windows. diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs index 79756096973be..8a84756da35dd 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/Accept.cs @@ -222,7 +222,7 @@ public async Task Accept_WithTargetSocket_ReuseAfterDisconnect_Success(bool reus [OuterLoop] [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/1483", TestPlatforms.AnyUnix)] - public void Accept_WithAlreadyBoundTargetSocket_Fails() + public async Task Accept_WithAlreadyBoundTargetSocket_Fails() { if (!SupportsAcceptIntoExistingSocket) return; @@ -236,7 +236,7 @@ public void Accept_WithAlreadyBoundTargetSocket_Fails() server.BindToAnonymousPort(IPAddress.Loopback); - Assert.Throws(() => { AcceptAsync(listener, server); }); + await Assert.ThrowsAsync(() => AcceptAsync(listener, server)); } } @@ -262,7 +262,7 @@ public async Task Accept_WithInUseTargetSocket_Fails() Assert.Same(server, accepted); Assert.True(accepted.Connected); - Assert.Throws(() => { AcceptAsync(listener, server); }); + await Assert.ThrowsAsync(() => AcceptAsync(listener, server)); } } @@ -353,8 +353,7 @@ await RetryHelper.ExecuteAsync(async () => } [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public async Task AcceptReceive_Windows_Success() + public async Task AcceptReceive_Success() { if (!SupportsAcceptReceive) { @@ -376,24 +375,6 @@ public async Task AcceptReceive_Windows_Success() (_, byte[] recvBuffer) = await acceptTask; Assert.Equal(new byte[] { 42 }, recvBuffer); } - - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public void AcceptReceive_Unix_ThrowsPlatformNotSupportedException() - { - if (!SupportsAcceptReceive) - { - // Currently only supported by APM and EAP - return; - } - - using Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - int port = listener.BindToAnonymousPort(IPAddress.Loopback); - IPEndPoint listenerEndpoint = new IPEndPoint(IPAddress.Loopback, port); - listener.Listen(100); - - Assert.ThrowsAsync(() => AcceptAsync(listener, 1) ); - } } public sealed class AcceptSync : Accept @@ -409,22 +390,6 @@ public AcceptSyncForceNonBlocking(ITestOutputHelper output) : base(output) {} public sealed class AcceptApm : Accept { public AcceptApm(ITestOutputHelper output) : base(output) {} - - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public void EndAccept_AcceptReceiveUnix_ThrowsPlatformNotSupportedException() - { - using Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - - // Creating a fake IAsyncResult: - int port = listener.BindToAnonymousPort(IPAddress.Loopback); - IPEndPoint listenerEndpoint = new IPEndPoint(IPAddress.Loopback, port); - listener.Listen(100); - IAsyncResult iar = listener.BeginAccept(callback: null, state: null); - - Assert.Throws(() => listener.EndAccept(out _, iar)); - Assert.Throws(() => listener.EndAccept(out _, out _, iar)); - } } public sealed class AcceptTask : Accept diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs index 1d343cb434f6d..1c0270eaca6bd 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/DisconnectTest.cs @@ -120,27 +120,6 @@ public void EndDisconnect_InvalidArguments_Throws() AssertExtensions.Throws("asyncResult", () => s.EndDisconnect(Task.CompletedTask)); } } - - [Fact] - public void BeginDisconnect_NotConnected_ThrowSync() - { - using (Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - { - Assert.Throws(() => s.BeginDisconnect(true, null, null)); - Assert.Throws(() => s.BeginDisconnect(false, null, null)); - } - } - - [Fact] - public void BeginDisconnection_ObjectDisposed_ThrowSync() - { - using (Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) - { - s.Dispose(); - Assert.Throws(() => s.BeginDisconnect(true, null, null)); - Assert.Throws(() => s.BeginDisconnect(false, null, null)); - } - } } public sealed class Disconnect_Task : Disconnect diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs index b1de69b736601..983f7a23bb1a8 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs @@ -139,7 +139,10 @@ public override Task AcceptAsync(Socket s) => return (socket, buffer); } public override Task AcceptAsync(Socket s, Socket acceptSocket) => - Task.Factory.FromAsync(s.BeginAccept, s.EndAccept, acceptSocket, 0, null); + Task.Factory.FromAsync( + (callback, state) => s.BeginAccept(acceptSocket, 0, callback, state), + result => s.EndAccept(out _, out _, result), + null); public override Task ConnectAsync(Socket s, EndPoint endPoint) => Task.Factory.FromAsync(s.BeginConnect, s.EndConnect, endPoint, null); public override Task MultiConnectAsync(Socket s, IPAddress[] addresses, int port) => @@ -291,7 +294,7 @@ public sealed class SocketHelperEap : SocketHelperBase { public override bool UsesEap => true; public override bool ValidatesArrayArguments => false; - public override bool SupportsAcceptReceive => true; + public override bool SupportsAcceptReceive => PlatformDetection.IsWindows; public override Task AcceptAsync(Socket s) => InvokeAsync(s, e => e.AcceptSocket, e => s.AcceptAsync(e));