From f22e8e813a5fdd2f28c68eda62c7c518755593b2 Mon Sep 17 00:00:00 2001 From: Geoffrey Kizer Date: Fri, 28 Aug 2020 02:55:43 -0700 Subject: [PATCH 01/42] SendToAsync --- .../ref/System.Net.Sockets.cs | 1 + .../src/System/Net/Sockets/Socket.Tasks.cs | 46 ++++++++++++++++--- .../src/System/Net/Sockets/Socket.cs | 6 ++- .../Net/Sockets/SocketAsyncEventArgs.Unix.cs | 4 +- .../Sockets/SocketAsyncEventArgs.Windows.cs | 8 ++-- .../Net/Sockets/SocketTaskExtensions.cs | 3 ++ 6 files changed, 53 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs index e965b80cef0ea..777d467759bb7 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -571,6 +571,7 @@ public static partial class SocketTaskExtensions public static System.Threading.Tasks.Task SendAsync(this System.Net.Sockets.Socket socket, System.Collections.Generic.IList> buffers, System.Net.Sockets.SocketFlags socketFlags) { throw null; } public static System.Threading.Tasks.ValueTask SendAsync(this System.Net.Sockets.Socket socket, System.ReadOnlyMemory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task SendToAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; } + public static System.Threading.Tasks.ValueTask SendToAsync(this System.Net.Sockets.Socket socket, System.ReadOnlyMemory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } public enum SocketType { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index 83b8e3e4bb737..093ba78d448e1 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -318,14 +318,27 @@ internal Task SendAsync(IList> buffers, SocketFlags sock internal Task SendToAsync(ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEP) { - var tcs = new TaskCompletionSource(this); - BeginSendTo(buffer.Array!, buffer.Offset, buffer.Count, socketFlags, remoteEP, iar => + ValidateBuffer(buffer); + return SendToAsync(buffer, socketFlags, remoteEP, default).AsTask(); + } + + internal ValueTask SendToAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken) + { + if (remoteEP is null) { - var innerTcs = (TaskCompletionSource)iar.AsyncState!; - try { innerTcs.TrySetResult(((Socket)innerTcs.Task.AsyncState!).EndSendTo(iar)); } - catch (Exception e) { innerTcs.TrySetException(e); } - }, tcs); - return tcs.Task; + throw new ArgumentNullException(nameof(remoteEP)); + } + + AwaitableSocketAsyncEventArgs saea = + Interlocked.Exchange(ref _singleBufferSendEventArgs, null) ?? + new AwaitableSocketAsyncEventArgs(this, isReceiveForCaching: false); + + Debug.Assert(saea.BufferList == null); + saea.SetBuffer(MemoryMarshal.AsMemory(buffer)); + saea.SocketFlags = socketFlags; + saea.RemoteEndPoint = remoteEP; + saea.WrapExceptionsInNetworkExceptions = false; + return saea.SendToAsync(this); } /// Validates the supplied array segment, throwing if its array or indices are null or out-of-bounds, respectively. @@ -729,6 +742,25 @@ public ValueTask SendAsyncForNetworkStream(Socket socket, CancellationToken canc ValueTask.FromException(CreateException(error)); } + public ValueTask SendToAsync(Socket socket) + { + Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); + + if (socket.SendToAsync(this)) + { + return new ValueTask(this, _token); + } + + int bytesTransferred = BytesTransferred; + SocketError error = SocketError; + + Release(); + + return error == SocketError.Success ? + new ValueTask(bytesTransferred) : + ValueTask.FromException(CreateException(error)); + } + public ValueTask ConnectAsync(Socket socket) { Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); 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 9fbc3c4c9a28b..aa30911ab4b12 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 @@ -4057,7 +4057,9 @@ public bool SendPacketsAsync(SocketAsyncEventArgs e) return socketError == SocketError.IOPending; } - public bool SendToAsync(SocketAsyncEventArgs e) + public bool SendToAsync(SocketAsyncEventArgs e) => SendToAsync(e, default); + + private bool SendToAsync(SocketAsyncEventArgs e, CancellationToken cancellationToken) { ThrowIfDisposed(); @@ -4086,7 +4088,7 @@ public bool SendToAsync(SocketAsyncEventArgs e) SocketError socketError; try { - socketError = e.DoOperationSendTo(_handle); + socketError = e.DoOperationSendTo(_handle, cancellationToken); } catch { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs index 09d688d9e0c70..92b61c8a32092 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs @@ -303,7 +303,7 @@ internal SocketError DoOperationSendPackets(Socket socket, SafeSocketHandle hand return SocketError.IOPending; } - internal SocketError DoOperationSendTo(SafeSocketHandle handle) + internal SocketError DoOperationSendTo(SafeSocketHandle handle, CancellationToken cancellationToken) { _receivedFlags = System.Net.Sockets.SocketFlags.None; _socketAddressSize = 0; @@ -313,7 +313,7 @@ internal SocketError DoOperationSendTo(SafeSocketHandle handle) SocketError errorCode; if (_bufferList == null) { - errorCode = handle.AsyncContext.SendToAsync(_buffer, _offset, _count, _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesSent, TransferCompletionCallback); + errorCode = handle.AsyncContext.SendToAsync(_buffer, _offset, _count, _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesSent, TransferCompletionCallback, cancellationToken); } else { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs index 6368666ec6bf4..fd2f46277d9d6 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs @@ -720,7 +720,7 @@ internal unsafe SocketError DoOperationSendPackets(Socket socket, SafeSocketHand } } - internal unsafe SocketError DoOperationSendTo(SafeSocketHandle handle) + internal unsafe SocketError DoOperationSendTo(SafeSocketHandle handle, CancellationToken cancellationToken) { // WSASendTo uses a WSABuffer array describing buffers in which to // receive data and from which to send data respectively. Single and multiple buffers @@ -731,11 +731,11 @@ internal unsafe SocketError DoOperationSendTo(SafeSocketHandle handle) PinSocketAddressBuffer(); return _bufferList == null ? - DoOperationSendToSingleBuffer(handle) : + DoOperationSendToSingleBuffer(handle, cancellationToken) : DoOperationSendToMultiBuffer(handle); } - internal unsafe SocketError DoOperationSendToSingleBuffer(SafeSocketHandle handle) + internal unsafe SocketError DoOperationSendToSingleBuffer(SafeSocketHandle handle, CancellationToken cancellationToken) { fixed (byte* bufferPtr = &MemoryMarshal.GetReference(_buffer.Span)) { @@ -757,7 +757,7 @@ internal unsafe SocketError DoOperationSendToSingleBuffer(SafeSocketHandle handl overlapped, IntPtr.Zero); - return ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred, overlapped); + return ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred, overlapped, cancellationToken); } catch { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs index f6a2243e626df..5d56a07aa5f08 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs @@ -48,7 +48,10 @@ public static ValueTask SendAsync(this Socket socket, ReadOnlyMemory socket.SendAsync(buffer, socketFlags, cancellationToken); public static Task SendAsync(this Socket socket, IList> buffers, SocketFlags socketFlags) => socket.SendAsync(buffers, socketFlags); + public static Task SendToAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEP) => socket.SendToAsync(buffer, socketFlags, remoteEP); + public static ValueTask SendToAsync(this Socket socket, ReadOnlyMemory buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = default) => + socket.SendToAsync(buffer, socketFlags, remoteEP, cancellationToken); } } From 6b07fb2a642d65adbfe3dd88d63820dc168c2b81 Mon Sep 17 00:00:00 2001 From: Geoffrey Kizer Date: Fri, 28 Aug 2020 03:03:44 -0700 Subject: [PATCH 02/42] better tests --- .../tests/FunctionalTests/SocketTestHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs index 41fa116b0c40c..c67a338d7f640 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs @@ -207,7 +207,7 @@ public override Task SendAsync(Socket s, ArraySegment buffer) => public override Task SendAsync(Socket s, IList> bufferList) => s.SendAsync(bufferList, SocketFlags.None); public override Task SendToAsync(Socket s, ArraySegment buffer, EndPoint endPoint) => - s.SendToAsync(buffer, SocketFlags.None, endPoint); + s.SendToAsync(buffer, SocketFlags.None, endPoint, _cts.Token).AsTask() ; } public sealed class SocketHelperEap : SocketHelperBase From cdade756d5ddc17bae59d4843c699633651ea591 Mon Sep 17 00:00:00 2001 From: Geoffrey Kizer Date: Sun, 30 Aug 2020 22:08:48 -0700 Subject: [PATCH 03/42] fix SendToAsync cancellation --- .../src/System/Net/Sockets/Socket.Tasks.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index 093ba78d448e1..1d43c5f95e208 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -338,7 +338,7 @@ internal ValueTask SendToAsync(ReadOnlyMemory buffer, SocketFlags soc saea.SocketFlags = socketFlags; saea.RemoteEndPoint = remoteEP; saea.WrapExceptionsInNetworkExceptions = false; - return saea.SendToAsync(this); + return saea.SendToAsync(this, cancellationToken); } /// Validates the supplied array segment, throwing if its array or indices are null or out-of-bounds, respectively. @@ -742,12 +742,13 @@ public ValueTask SendAsyncForNetworkStream(Socket socket, CancellationToken canc ValueTask.FromException(CreateException(error)); } - public ValueTask SendToAsync(Socket socket) + public ValueTask SendToAsync(Socket socket, CancellationToken cancellationToken) { Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); - if (socket.SendToAsync(this)) + if (socket.SendToAsync(this, cancellationToken)) { + _cancellationToken = cancellationToken; return new ValueTask(this, _token); } From 5345d55f72a60850bb2554c066d4da6e50e2915d Mon Sep 17 00:00:00 2001 From: Geoffrey Kizer Date: Mon, 31 Aug 2020 20:37:34 -0700 Subject: [PATCH 04/42] implement ReceiveFromAsync --- .../ref/System.Net.Sockets.cs | 1 + .../src/System/Net/Sockets/Socket.Tasks.cs | 84 ++++++++++++++++++- .../src/System/Net/Sockets/Socket.cs | 6 +- .../Net/Sockets/SocketAsyncEventArgs.Unix.cs | 4 +- .../Sockets/SocketAsyncEventArgs.Windows.cs | 8 +- .../Net/Sockets/SocketTaskExtensions.cs | 4 + .../ArgumentValidationTests.cs | 2 +- 7 files changed, 99 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs index 777d467759bb7..c0426e05ca479 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -566,6 +566,7 @@ public static partial class SocketTaskExtensions public static System.Threading.Tasks.Task ReceiveAsync(this System.Net.Sockets.Socket socket, System.Collections.Generic.IList> buffers, System.Net.Sockets.SocketFlags socketFlags) { throw null; } public static System.Threading.Tasks.ValueTask ReceiveAsync(this System.Net.Sockets.Socket socket, System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task ReceiveFromAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint) { throw null; } + public static System.Threading.Tasks.ValueTask ReceiveFromAsync(this System.Net.Sockets.Socket socket, System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task ReceiveMessageFromAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint) { throw null; } public static System.Threading.Tasks.Task SendAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags) { throw null; } public static System.Threading.Tasks.Task SendAsync(this System.Net.Sockets.Socket socket, System.Collections.Generic.IList> buffers, System.Net.Sockets.SocketFlags socketFlags) { throw null; } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index 1d43c5f95e208..8c7d9c54a0b42 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -217,6 +217,42 @@ internal Task ReceiveAsync(IList> buffers, SocketFlags s internal Task ReceiveFromAsync(ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEndPoint) { + ValidateBuffer(buffer); + return ReceiveFromAsync(buffer, socketFlags, remoteEndPoint, default).AsTask(); + } + + internal ValueTask ReceiveFromAsync(Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken) + { + if (remoteEndPoint is null) + { + throw new ArgumentNullException(nameof(remoteEndPoint)); + } + if (!CanTryAddressFamily(remoteEndPoint.AddressFamily)) + { + throw new ArgumentException(SR.Format(SR.net_InvalidEndPointAddressFamily, remoteEndPoint.AddressFamily, _addressFamily), nameof(remoteEndPoint)); + } + if (_rightEndPoint == null) + { + throw new InvalidOperationException(SR.net_sockets_mustbind); + } + + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + AwaitableSocketAsyncEventArgs saea = + Interlocked.Exchange(ref _singleBufferReceiveEventArgs, null) ?? + new AwaitableSocketAsyncEventArgs(this, isReceiveForCaching: true); + + Debug.Assert(saea.BufferList == null); + saea.SetBuffer(buffer); + saea.SocketFlags = socketFlags; + saea.RemoteEndPoint = remoteEndPoint; + saea.WrapExceptionsInNetworkExceptions = false; + return saea.ReceiveFromAsync(this, cancellationToken); + +#if false var tcs = new StateTaskCompletionSource(this) { _field1 = remoteEndPoint }; BeginReceiveFrom(buffer.Array!, buffer.Offset, buffer.Count, socketFlags, ref tcs._field1, iar => { @@ -233,6 +269,7 @@ internal Task ReceiveFromAsync(ArraySegment buffe catch (Exception e) { innerTcs.TrySetException(e); } }, tcs); return tcs.Task; +#endif } internal Task ReceiveMessageFromAsync(ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEndPoint) @@ -599,7 +636,7 @@ internal AsyncTaskMethodBuilder GetCompletionResponsibility(out bool re } /// A SocketAsyncEventArgs that can be awaited to get the result of an operation. - internal sealed class AwaitableSocketAsyncEventArgs : SocketAsyncEventArgs, IValueTaskSource, IValueTaskSource + internal sealed class AwaitableSocketAsyncEventArgs : SocketAsyncEventArgs, IValueTaskSource, IValueTaskSource, IValueTaskSource { private static readonly Action s_completedSentinel = new Action(state => throw new InvalidOperationException(SR.Format(SR.net_sockets_valuetaskmisuse, nameof(s_completedSentinel)))); /// The owning socket. @@ -701,6 +738,29 @@ public ValueTask ReceiveAsync(Socket socket, CancellationToken cancellation ValueTask.FromException(CreateException(error)); } + /// Initiates a receive operation on the associated socket. + /// This instance. + public ValueTask ReceiveFromAsync(Socket socket, CancellationToken cancellationToken) + { + Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); + + if (socket.ReceiveFromAsync(this, cancellationToken)) + { + _cancellationToken = cancellationToken; + return new ValueTask(this, _token); + } + + int bytesTransferred = BytesTransferred; + EndPoint remoteEndPoint = RemoteEndPoint!; + SocketError error = SocketError; + + Release(); + + return error == SocketError.Success ? + new ValueTask(new SocketReceiveFromResult() { ReceivedBytes = bytesTransferred, RemoteEndPoint = remoteEndPoint }) : + ValueTask.FromException(CreateException(error)); + } + /// Initiates a send operation on the associated socket. /// This instance. public ValueTask SendAsync(Socket socket, CancellationToken cancellationToken) @@ -936,6 +996,28 @@ void IValueTaskSource.GetResult(short token) } } + SocketReceiveFromResult IValueTaskSource.GetResult(short token) + { + if (token != _token) + { + ThrowIncorrectTokenException(); + } + + SocketError error = SocketError; + int bytes = BytesTransferred; + EndPoint remoteEndPoint = RemoteEndPoint!; + CancellationToken cancellationToken = _cancellationToken; + + Release(); + + if (error != SocketError.Success) + { + ThrowException(error, cancellationToken); + } + + return new SocketReceiveFromResult() { ReceivedBytes = bytes, RemoteEndPoint = remoteEndPoint }; + } + private void ThrowIncorrectTokenException() => throw new InvalidOperationException(SR.InvalidOperation_IncorrectToken); private void ThrowMultipleContinuationsException() => throw new InvalidOperationException(SR.InvalidOperation_MultipleContinuations); 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 aa30911ab4b12..0b5ed14f6a226 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 @@ -3900,7 +3900,9 @@ private bool ReceiveAsync(SocketAsyncEventArgs e, CancellationToken cancellation return socketError == SocketError.IOPending; } - public bool ReceiveFromAsync(SocketAsyncEventArgs e) + public bool ReceiveFromAsync(SocketAsyncEventArgs e) => ReceiveFromAsync(e, default); + + private bool ReceiveFromAsync(SocketAsyncEventArgs e, CancellationToken cancellationToken) { ThrowIfDisposed(); @@ -3934,7 +3936,7 @@ public bool ReceiveFromAsync(SocketAsyncEventArgs e) SocketError socketError; try { - socketError = e.DoOperationReceiveFrom(_handle); + socketError = e.DoOperationReceiveFrom(_handle, cancellationToken); } catch { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs index 92b61c8a32092..89699c3bbddfa 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs @@ -153,7 +153,7 @@ internal unsafe SocketError DoOperationReceive(SafeSocketHandle handle, Cancella return errorCode; } - internal unsafe SocketError DoOperationReceiveFrom(SafeSocketHandle handle) + internal unsafe SocketError DoOperationReceiveFrom(SafeSocketHandle handle, CancellationToken cancellationToken) { _receivedFlags = System.Net.Sockets.SocketFlags.None; _socketAddressSize = 0; @@ -164,7 +164,7 @@ internal unsafe SocketError DoOperationReceiveFrom(SafeSocketHandle handle) int socketAddressLen = _socketAddress!.Size; if (_bufferList == null) { - errorCode = handle.AsyncContext.ReceiveFromAsync(_buffer.Slice(_offset, _count), _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesReceived, out flags, TransferCompletionCallback); + errorCode = handle.AsyncContext.ReceiveFromAsync(_buffer.Slice(_offset, _count), _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesReceived, out flags, TransferCompletionCallback, cancellationToken); } else { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs index fd2f46277d9d6..9f7ea0864deab 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs @@ -391,7 +391,7 @@ internal unsafe SocketError DoOperationReceiveMultiBuffer(SafeSocketHandle handl } } - internal unsafe SocketError DoOperationReceiveFrom(SafeSocketHandle handle) + internal unsafe SocketError DoOperationReceiveFrom(SafeSocketHandle handle, CancellationToken cancellationToken) { // WSARecvFrom uses a WSABuffer array describing buffers in which to // receive data and from which to send data respectively. Single and multiple buffers @@ -401,11 +401,11 @@ internal unsafe SocketError DoOperationReceiveFrom(SafeSocketHandle handle) PinSocketAddressBuffer(); return _bufferList == null ? - DoOperationReceiveFromSingleBuffer(handle) : + DoOperationReceiveFromSingleBuffer(handle, cancellationToken) : DoOperationReceiveFromMultiBuffer(handle); } - internal unsafe SocketError DoOperationReceiveFromSingleBuffer(SafeSocketHandle handle) + internal unsafe SocketError DoOperationReceiveFromSingleBuffer(SafeSocketHandle handle, CancellationToken cancellationToken) { fixed (byte* bufferPtr = &MemoryMarshal.GetReference(_buffer.Span)) { @@ -428,7 +428,7 @@ internal unsafe SocketError DoOperationReceiveFromSingleBuffer(SafeSocketHandle overlapped, IntPtr.Zero); - return ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred, overlapped); + return ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred, overlapped, cancellationToken); } catch { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs index 5d56a07aa5f08..a2ec5a48b3873 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs @@ -37,8 +37,12 @@ public static ValueTask ReceiveAsync(this Socket socket, Memory buffe socket.ReceiveAsync(buffer, socketFlags, fromNetworkStream: false, cancellationToken: cancellationToken); public static Task ReceiveAsync(this Socket socket, IList> buffers, SocketFlags socketFlags) => socket.ReceiveAsync(buffers, socketFlags); + public static Task ReceiveFromAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEndPoint) => socket.ReceiveFromAsync(buffer, socketFlags, remoteEndPoint); + public static ValueTask ReceiveFromAsync(this Socket socket, Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) => + socket.ReceiveFromAsync(buffer, socketFlags, remoteEndPoint, cancellationToken); + public static Task ReceiveMessageFromAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEndPoint) => socket.ReceiveMessageFromAsync(buffer, socketFlags, remoteEndPoint); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs index accf15c3ec7f5..139bd39563ef4 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs @@ -1334,7 +1334,7 @@ public void BeginReceiveFrom_AddressFamily_Throws_Argument() { EndPoint endpoint = new IPEndPoint(IPAddress.IPv6Loopback, 1); AssertExtensions.Throws("remoteEP", () => GetSocket(AddressFamily.InterNetwork).BeginReceiveFrom(s_buffer, 0, 0, SocketFlags.None, ref endpoint, TheAsyncCallback, null)); - AssertExtensions.Throws("remoteEP", () => { GetSocket(AddressFamily.InterNetwork).ReceiveFromAsync(new ArraySegment(s_buffer, 0, 0), SocketFlags.None, endpoint); }); + AssertExtensions.Throws("remoteEndPoint", () => { GetSocket(AddressFamily.InterNetwork).ReceiveFromAsync(new ArraySegment(s_buffer, 0, 0), SocketFlags.None, endpoint); }); } [Fact] From 45f5544d76a77351f73fe578b983849b79e43902 Mon Sep 17 00:00:00 2001 From: Geoffrey Kizer Date: Mon, 31 Aug 2020 20:59:18 -0700 Subject: [PATCH 05/42] ReceiveMessageFrom, WIP --- .../src/System/Net/Sockets/Socket.Tasks.cs | 121 ++++++++++++------ .../src/System/Net/Sockets/Socket.cs | 6 +- .../ArgumentValidationTests.cs | 2 +- 3 files changed, 87 insertions(+), 42 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index 8c7d9c54a0b42..494c12987e9d2 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -251,48 +251,44 @@ internal ValueTask ReceiveFromAsync(Memory buffer saea.RemoteEndPoint = remoteEndPoint; saea.WrapExceptionsInNetworkExceptions = false; return saea.ReceiveFromAsync(this, cancellationToken); - -#if false - var tcs = new StateTaskCompletionSource(this) { _field1 = remoteEndPoint }; - BeginReceiveFrom(buffer.Array!, buffer.Offset, buffer.Count, socketFlags, ref tcs._field1, iar => - { - var innerTcs = (StateTaskCompletionSource)iar.AsyncState!; - try - { - int receivedBytes = ((Socket)innerTcs.Task.AsyncState!).EndReceiveFrom(iar, ref innerTcs._field1); - innerTcs.TrySetResult(new SocketReceiveFromResult - { - ReceivedBytes = receivedBytes, - RemoteEndPoint = innerTcs._field1 - }); - } - catch (Exception e) { innerTcs.TrySetException(e); } - }, tcs); - return tcs.Task; -#endif } internal Task ReceiveMessageFromAsync(ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEndPoint) { - var tcs = new StateTaskCompletionSource(this) { _field1 = socketFlags, _field2 = remoteEndPoint }; - BeginReceiveMessageFrom(buffer.Array!, buffer.Offset, buffer.Count, socketFlags, ref tcs._field2, iar => + ValidateBuffer(buffer); + return ReceiveMessageFromAsync(buffer, socketFlags, remoteEndPoint, default).AsTask(); + } + + internal ValueTask ReceiveMessageFromAsync(Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken) + { + if (remoteEndPoint is null) { - var innerTcs = (StateTaskCompletionSource)iar.AsyncState!; - try - { - IPPacketInformation ipPacketInformation; - int receivedBytes = ((Socket)innerTcs.Task.AsyncState!).EndReceiveMessageFrom(iar, ref innerTcs._field1, ref innerTcs._field2, out ipPacketInformation); - innerTcs.TrySetResult(new SocketReceiveMessageFromResult - { - ReceivedBytes = receivedBytes, - RemoteEndPoint = innerTcs._field2, - SocketFlags = innerTcs._field1, - PacketInformation = ipPacketInformation - }); - } - catch (Exception e) { innerTcs.TrySetException(e); } - }, tcs); - return tcs.Task; + throw new ArgumentNullException(nameof(remoteEndPoint)); + } + if (!CanTryAddressFamily(remoteEndPoint.AddressFamily)) + { + throw new ArgumentException(SR.Format(SR.net_InvalidEndPointAddressFamily, remoteEndPoint.AddressFamily, _addressFamily), nameof(remoteEndPoint)); + } + if (_rightEndPoint == null) + { + throw new InvalidOperationException(SR.net_sockets_mustbind); + } + + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + + AwaitableSocketAsyncEventArgs saea = + Interlocked.Exchange(ref _singleBufferReceiveEventArgs, null) ?? + new AwaitableSocketAsyncEventArgs(this, isReceiveForCaching: true); + + Debug.Assert(saea.BufferList == null); + saea.SetBuffer(buffer); + saea.SocketFlags = socketFlags; + saea.RemoteEndPoint = remoteEndPoint; + saea.WrapExceptionsInNetworkExceptions = false; + return saea.ReceiveMessageFromAsync(this, cancellationToken); } internal Task SendAsync(ArraySegment buffer, SocketFlags socketFlags) @@ -636,7 +632,7 @@ internal AsyncTaskMethodBuilder GetCompletionResponsibility(out bool re } /// A SocketAsyncEventArgs that can be awaited to get the result of an operation. - internal sealed class AwaitableSocketAsyncEventArgs : SocketAsyncEventArgs, IValueTaskSource, IValueTaskSource, IValueTaskSource + internal sealed class AwaitableSocketAsyncEventArgs : SocketAsyncEventArgs, IValueTaskSource, IValueTaskSource, IValueTaskSource, IValueTaskSource { private static readonly Action s_completedSentinel = new Action(state => throw new InvalidOperationException(SR.Format(SR.net_sockets_valuetaskmisuse, nameof(s_completedSentinel)))); /// The owning socket. @@ -738,8 +734,6 @@ public ValueTask ReceiveAsync(Socket socket, CancellationToken cancellation ValueTask.FromException(CreateException(error)); } - /// Initiates a receive operation on the associated socket. - /// This instance. public ValueTask ReceiveFromAsync(Socket socket, CancellationToken cancellationToken) { Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); @@ -761,6 +755,29 @@ public ValueTask ReceiveFromAsync(Socket socket, Cancel ValueTask.FromException(CreateException(error)); } + public ValueTask ReceiveMessageFromAsync(Socket socket, CancellationToken cancellationToken) + { + Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); + + if (socket.ReceiveMessageFromAsync(this, cancellationToken)) + { + _cancellationToken = cancellationToken; + return new ValueTask(this, _token); + } + + int bytesTransferred = BytesTransferred; + EndPoint remoteEndPoint = RemoteEndPoint!; + SocketFlags socketFlags = SocketFlags; + IPPacketInformation packetInformation = ReceiveMessageFromPacketInfo; + SocketError error = SocketError; + + Release(); + + return error == SocketError.Success ? + new ValueTask(new SocketReceiveMessageFromResult() { ReceivedBytes = bytesTransferred, RemoteEndPoint = remoteEndPoint, SocketFlags = socketFlags, PacketInformation = packetInformation }) : + ValueTask.FromException(CreateException(error)); + } + /// Initiates a send operation on the associated socket. /// This instance. public ValueTask SendAsync(Socket socket, CancellationToken cancellationToken) @@ -1018,6 +1035,30 @@ SocketReceiveFromResult IValueTaskSource.GetResult(shor return new SocketReceiveFromResult() { ReceivedBytes = bytes, RemoteEndPoint = remoteEndPoint }; } + SocketReceiveMessageFromResult IValueTaskSource.GetResult(short token) + { + if (token != _token) + { + ThrowIncorrectTokenException(); + } + + SocketError error = SocketError; + int bytes = BytesTransferred; + EndPoint remoteEndPoint = RemoteEndPoint!; + SocketFlags socketFlags = SocketFlags; + IPPacketInformation packetInformation = ReceiveMessageFromPacketInfo; + CancellationToken cancellationToken = _cancellationToken; + + Release(); + + if (error != SocketError.Success) + { + ThrowException(error, cancellationToken); + } + + return new SocketReceiveMessageFromResult() { ReceivedBytes = bytes, RemoteEndPoint = remoteEndPoint, SocketFlags = socketFlags, PacketInformation = packetInformation }; + } + private void ThrowIncorrectTokenException() => throw new InvalidOperationException(SR.InvalidOperation_IncorrectToken); private void ThrowMultipleContinuationsException() => throw new InvalidOperationException(SR.InvalidOperation_MultipleContinuations); 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 0b5ed14f6a226..5f5dfd3521eea 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 @@ -3949,7 +3949,9 @@ private bool ReceiveFromAsync(SocketAsyncEventArgs e, CancellationToken cancella return pending; } - public bool ReceiveMessageFromAsync(SocketAsyncEventArgs e) + public bool ReceiveMessageFromAsync(SocketAsyncEventArgs e) => ReceiveMessageFromAsync(e, default); + + private bool ReceiveMessageFromAsync(SocketAsyncEventArgs e, CancellationToken cancellationToken) { ThrowIfDisposed(); @@ -3980,6 +3982,8 @@ public bool ReceiveMessageFromAsync(SocketAsyncEventArgs e) SetReceivingPacketInformation(); + // TODO: Handle cancellation + // Prepare for and make the native call. e.StartOperationCommon(this, SocketAsyncOperation.ReceiveMessageFrom); SocketError socketError; diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs index 139bd39563ef4..62baffe5b24ed 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ArgumentValidationTests.cs @@ -1402,7 +1402,7 @@ public void BeginReceiveMessageFrom_AddressFamily_Throws_Argument() EndPoint remote = new IPEndPoint(IPAddress.IPv6Loopback, 1); AssertExtensions.Throws("remoteEP", () => GetSocket(AddressFamily.InterNetwork).BeginReceiveMessageFrom(s_buffer, 0, 0, SocketFlags.None, ref remote, TheAsyncCallback, null)); - AssertExtensions.Throws("remoteEP", () => { GetSocket(AddressFamily.InterNetwork).ReceiveMessageFromAsync(new ArraySegment(s_buffer, 0, 0), SocketFlags.None, remote); }); + AssertExtensions.Throws("remoteEndPoint", () => { GetSocket(AddressFamily.InterNetwork).ReceiveMessageFromAsync(new ArraySegment(s_buffer, 0, 0), SocketFlags.None, remote); }); } [Fact] From 09c8cf999aace08bc25a361397282990a3e62f5a Mon Sep 17 00:00:00 2001 From: Geoffrey Kizer Date: Mon, 31 Aug 2020 21:09:53 -0700 Subject: [PATCH 06/42] add public overload --- src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs | 1 + .../src/System/Net/Sockets/SocketTaskExtensions.cs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs index c0426e05ca479..998c4eebb605a 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -568,6 +568,7 @@ public static partial class SocketTaskExtensions public static System.Threading.Tasks.Task ReceiveFromAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint) { throw null; } public static System.Threading.Tasks.ValueTask ReceiveFromAsync(this System.Net.Sockets.Socket socket, System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public static System.Threading.Tasks.Task ReceiveMessageFromAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint) { throw null; } + public static System.Threading.Tasks.ValueTask ReceiveMessageFromAsync(this System.Net.Sockets.Socket socket, System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint, System.Threading.CancellationToken cancellationToken = default) { throw null; } public static System.Threading.Tasks.Task SendAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags) { throw null; } public static System.Threading.Tasks.Task SendAsync(this System.Net.Sockets.Socket socket, System.Collections.Generic.IList> buffers, System.Net.Sockets.SocketFlags socketFlags) { throw null; } public static System.Threading.Tasks.ValueTask SendAsync(this System.Net.Sockets.Socket socket, System.ReadOnlyMemory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs index a2ec5a48b3873..0aac67eecd27d 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs @@ -45,6 +45,9 @@ public static ValueTask ReceiveFromAsync(this Socket so public static Task ReceiveMessageFromAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEndPoint) => socket.ReceiveMessageFromAsync(buffer, socketFlags, remoteEndPoint); + public static ValueTask ReceiveMessageFromAsync(this Socket socket, Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) => + socket.ReceiveMessageFromAsync(buffer, socketFlags, remoteEndPoint, cancellationToken); + public static Task SendAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags) => socket.SendAsync(buffer, socketFlags); From a6402146d2af7d8c41233e9142a0f1fcbbf4a870 Mon Sep 17 00:00:00 2001 From: Geoffrey Kizer Date: Mon, 31 Aug 2020 21:18:04 -0700 Subject: [PATCH 07/42] proper cancellation --- .../System.Net.Sockets/src/System/Net/Sockets/Socket.cs | 4 +--- .../src/System/Net/Sockets/SocketAsyncContext.Unix.cs | 4 ++-- .../src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs | 4 ++-- .../src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs | 4 ++-- 4 files changed, 7 insertions(+), 9 deletions(-) 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 5f5dfd3521eea..173d7aff43531 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 @@ -3982,14 +3982,12 @@ private bool ReceiveMessageFromAsync(SocketAsyncEventArgs e, CancellationToken c SetReceivingPacketInformation(); - // TODO: Handle cancellation - // Prepare for and make the native call. e.StartOperationCommon(this, SocketAsyncOperation.ReceiveMessageFrom); SocketError socketError; try { - socketError = e.DoOperationReceiveMessageFrom(this, _handle); + socketError = e.DoOperationReceiveMessageFrom(this, _handle, cancellationToken); } catch { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs index 4abf6ff75c812..b8680b5966c5c 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs @@ -1736,7 +1736,7 @@ public SocketError ReceiveMessageFrom( return operation.ErrorCode; } - public SocketError ReceiveMessageFromAsync(Memory buffer, IList>? buffers, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out int bytesReceived, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, Action callback) + public SocketError ReceiveMessageFromAsync(Memory buffer, IList>? buffers, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out int bytesReceived, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, Action callback, CancellationToken cancellationToken = default) { SetNonBlocking(); @@ -1760,7 +1760,7 @@ public SocketError ReceiveMessageFromAsync(Memory buffer, IList Date: Tue, 3 Nov 2020 15:19:12 +0100 Subject: [PATCH 08/42] WrapExceptionsInNetworkExceptions -> WrapExceptionsForNetworkStream --- .../src/System/Net/Sockets/Socket.Tasks.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index c8df872a8c8ba..82cf93c7377c9 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -277,7 +277,7 @@ internal ValueTask ReceiveFromAsync(Memory buffer saea.SetBuffer(buffer); saea.SocketFlags = socketFlags; saea.RemoteEndPoint = remoteEndPoint; - saea.WrapExceptionsInNetworkExceptions = false; + saea.WrapExceptionsForNetworkStream = false; return saea.ReceiveFromAsync(this, cancellationToken); } @@ -315,7 +315,7 @@ internal ValueTask ReceiveMessageFromAsync(Memor saea.SetBuffer(buffer); saea.SocketFlags = socketFlags; saea.RemoteEndPoint = remoteEndPoint; - saea.WrapExceptionsInNetworkExceptions = false; + saea.WrapExceptionsForNetworkStream = false; return saea.ReceiveMessageFromAsync(this, cancellationToken); } @@ -398,7 +398,7 @@ internal ValueTask SendToAsync(ReadOnlyMemory buffer, SocketFlags soc saea.SetBuffer(MemoryMarshal.AsMemory(buffer)); saea.SocketFlags = socketFlags; saea.RemoteEndPoint = remoteEP; - saea.WrapExceptionsInNetworkExceptions = false; + saea.WrapExceptionsForNetworkStream = false; return saea.SendToAsync(this, cancellationToken); } From 614287bd80fdbd74f9e384996a1632ac720c6fe8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 15 Jan 2021 17:14:44 +0100 Subject: [PATCH 09/42] SocketHelperCancellableTask: pick the new overloads --- .../tests/FunctionalTests/SocketTestHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs index a5990b54c75b0..f4142b2a3da98 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs @@ -244,9 +244,9 @@ public override Task ReceiveAsync(Socket s, ArraySegment buffer) => public override Task ReceiveAsync(Socket s, IList> bufferList) => s.ReceiveAsync(bufferList, SocketFlags.None); public override Task ReceiveFromAsync(Socket s, ArraySegment buffer, EndPoint endPoint) => - s.ReceiveFromAsync(buffer, SocketFlags.None, endPoint); + s.ReceiveFromAsync(buffer, SocketFlags.None, endPoint, _cts.Token).AsTask(); public override Task ReceiveMessageFromAsync(Socket s, ArraySegment buffer, EndPoint endPoint) => - s.ReceiveMessageFromAsync(buffer, SocketFlags.None, endPoint); + s.ReceiveMessageFromAsync(buffer, SocketFlags.None, endPoint, _cts.Token).AsTask(); public override Task SendAsync(Socket s, ArraySegment buffer) => s.SendAsync(buffer, SocketFlags.None, _cts.Token).AsTask(); public override Task SendAsync(Socket s, IList> bufferList) => From 01b4d750cc9c391e77695d645f908bfad6efa216 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 15 Jan 2021 17:20:46 +0100 Subject: [PATCH 10/42] use SocketHelperCancellableTask in SendTo/Receive(Message)From tests --- .../tests/FunctionalTests/ReceiveMessageFrom.cs | 5 +++++ .../FunctionalTests/SendReceive/SendReceiveNonParallel.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index 6bc70e67a7fa8..de3ee73248b7f 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -74,6 +74,11 @@ public sealed class ReceiveMessageFrom_Task : ReceiveMessageFrom + { + public ReceiveMessageFrom_CancellableTask(ITestOutputHelper output) : base(output) { } + } + public sealed class ReceiveMessageFrom_Eap : ReceiveMessageFrom { public ReceiveMessageFrom_Eap(ITestOutputHelper output) : base(output) { } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveNonParallel.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveNonParallel.cs index 634d0f4854939..470472f226d4e 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveNonParallel.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendReceive/SendReceiveNonParallel.cs @@ -124,6 +124,11 @@ public sealed class SendReceiveNonParallel_Task : SendReceiveNonParallel + { + public SendReceiveNonParallel_CancellableTask(ITestOutputHelper output) : base(output) { } + } + public sealed class SendReceiveNonParallel_Eap : SendReceiveNonParallel { public SendReceiveNonParallel_Eap(ITestOutputHelper output) : base(output) { } From 2b76fdaf9cebba78b4c609da8cf4dd779c8bc001 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 15 Jan 2021 18:34:50 +0100 Subject: [PATCH 11/42] Dedicated tests for ReceiveFrom variants --- .../tests/FunctionalTests/ReceiveFrom.cs | 104 ++++++++++++++++++ .../tests/FunctionalTests/SocketTestHelper.cs | 2 + .../System.Net.Sockets.Tests.csproj | 1 + 3 files changed, 107 insertions(+) create mode 100644 src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs new file mode 100644 index 0000000000000..8354bbd79bd44 --- /dev/null +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace System.Net.Sockets.Tests +{ + public abstract class ReceiveFrom : SocketTestHelperBase where T : SocketHelperBase, new() + { + private static readonly IPEndPoint ValidUdpRemoteEndpoint = new IPEndPoint(IPAddress.Parse("10.20.30.40"), 1234); + + protected ReceiveFrom(ITestOutputHelper output) : base(output) { } + + [Theory] + [InlineData(1, -1, 0)] // offset low + [InlineData(1, 2, 0)] // offset high + [InlineData(1, 0, -1)] // count low + [InlineData(1, 1, 2)] // count high + public async Task OutOfRange_Throws(int length, int offset, int count) + { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + + ArraySegment buffer = new FakeArraySegment + { + Array = new byte[length], + Count = count, + Offset = offset + }.ToActual(); + + await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, buffer, ValidUdpRemoteEndpoint)); + } + + [Fact] + public async Task NullBuffer_Throws() + { + if (!ValidatesArrayArguments) return; + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + + await Assert.ThrowsAsync(() => ReceiveFromAsync(socket, null, ValidUdpRemoteEndpoint)); + } + + [Fact] + public async Task NullEndpoint_Throws() + { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + + await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, new byte[1], null)); + } + } + + public sealed class ReceiveFrom_Sync : ReceiveFrom + { + public ReceiveFrom_Sync(ITestOutputHelper output) : base(output) { } + } + + public sealed class ReceiveFrom_SyncForceNonBlocking : ReceiveFrom + { + public ReceiveFrom_SyncForceNonBlocking(ITestOutputHelper output) : base(output) { } + } + + public sealed class ReceiveFrom_Apm : ReceiveFrom + { + public ReceiveFrom_Apm(ITestOutputHelper output) : base(output) { } + } + + public sealed class ReceiveFrom_Task : ReceiveFrom + { + public ReceiveFrom_Task(ITestOutputHelper output) : base(output) { } + } + + public sealed class ReceiveFrom_CancellableTask : ReceiveFrom + { + public ReceiveFrom_CancellableTask(ITestOutputHelper output) : base(output) { } + } + + public sealed class ReceiveFrom_Eap : ReceiveFrom + { + public ReceiveFrom_Eap(ITestOutputHelper output) : base(output) { } + } + + public sealed class ReceiveFrom_SpanSync : ReceiveFrom + { + public ReceiveFrom_SpanSync(ITestOutputHelper output) : base(output) { } + } + + public sealed class ReceiveFrom_SpanSyncForceNonBlocking : ReceiveFrom + { + public ReceiveFrom_SpanSyncForceNonBlocking(ITestOutputHelper output) : base(output) { } + } + + public sealed class ReceiveFrom_MemoryArrayTask : ReceiveFrom + { + public ReceiveFrom_MemoryArrayTask(ITestOutputHelper output) : base(output) { } + } + + public sealed class ReceiveFrom_MemoryNativeTask : ReceiveFrom + { + public ReceiveFrom_MemoryNativeTask(ITestOutputHelper output) : base(output) { } + } +} diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs index f4142b2a3da98..2e3801f7a7f17 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SocketTestHelper.cs @@ -228,6 +228,8 @@ public class SocketHelperCancellableTask : SocketHelperBase { // Use a cancellable CancellationToken that we never cancel so that implementations can't just elide handling the CancellationToken. private readonly CancellationTokenSource _cts = new CancellationTokenSource(); + // This variant is typically working with Memory overloads. + public override bool ValidatesArrayArguments => false; public override Task AcceptAsync(Socket s) => s.AcceptAsync(); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj b/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj index 7ce628db90aae..f79246b4d34ef 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/System.Net.Sockets.Tests.csproj @@ -26,6 +26,7 @@ + From 9fce395a4d77ed2fd62befa2e494d0d59e8c5396 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 15 Jan 2021 18:54:27 +0100 Subject: [PATCH 12/42] ReceiveFrom cancellation test --- .../tests/FunctionalTests/ReceiveFrom.cs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 8354bbd79bd44..13541876098e7 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -11,7 +11,8 @@ namespace System.Net.Sockets.Tests { public abstract class ReceiveFrom : SocketTestHelperBase where T : SocketHelperBase, new() { - private static readonly IPEndPoint ValidUdpRemoteEndpoint = new IPEndPoint(IPAddress.Parse("10.20.30.40"), 1234); + protected static IPEndPoint GetGetDummyTestEndpoint(AddressFamily addressFamily = AddressFamily.InterNetwork) => + addressFamily == AddressFamily.InterNetwork ? new IPEndPoint(IPAddress.Parse("1.2.3.4"), 1234) : new IPEndPoint(IPAddress.Parse("1:2:3::4"), 1234); protected ReceiveFrom(ITestOutputHelper output) : base(output) { } @@ -31,7 +32,7 @@ public async Task OutOfRange_Throws(int length, int offset, int count) Offset = offset }.ToActual(); - await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, buffer, ValidUdpRemoteEndpoint)); + await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, buffer, GetGetDummyTestEndpoint())); } [Fact] @@ -40,7 +41,7 @@ public async Task NullBuffer_Throws() if (!ValidatesArrayArguments) return; using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - await Assert.ThrowsAsync(() => ReceiveFromAsync(socket, null, ValidUdpRemoteEndpoint)); + await Assert.ThrowsAsync(() => ReceiveFromAsync(socket, null, GetGetDummyTestEndpoint())); } [Fact] @@ -75,6 +76,23 @@ public ReceiveFrom_Task(ITestOutputHelper output) : base(output) { } public sealed class ReceiveFrom_CancellableTask : ReceiveFrom { public ReceiveFrom_CancellableTask(ITestOutputHelper output) : base(output) { } + + [Theory] + [MemberData(nameof(LoopbacksAndBuffers))] + public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) + { + using var socket = new Socket(loopback.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + socket.BindToAnonymousPort(loopback); + Memory buffer = new byte[1]; + + CancellationTokenSource cts = new CancellationTokenSource(); + if (precanceled) cts.Cancel(); + else cts.CancelAfter(10); + + OperationCanceledException ex = await Assert.ThrowsAnyAsync( + () => socket.ReceiveFromAsync(buffer, SocketFlags.None, GetGetDummyTestEndpoint(loopback.AddressFamily), cts.Token).AsTask()); + Assert.Equal(cts.Token, ex.CancellationToken); + } } public sealed class ReceiveFrom_Eap : ReceiveFrom From bcda9965b8d830ff6887a1660b2edc702ae75828 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 15 Jan 2021 18:59:24 +0100 Subject: [PATCH 13/42] Reformat SendTo tests --- .../tests/FunctionalTests/SendTo.cs | 82 +++++++++---------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index 3ff039c3e662c..ec5d3e6463ffb 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -82,57 +82,53 @@ public async Task Datagram_UDP_AccessDenied_Throws_DoesNotBind() } } - public static class SendTo + public sealed class SendTo_SyncSpan : SendToBase { - public static class Sync - { - public sealed class Span : SendToBase - { - public Span(ITestOutputHelper output) : base(output) { } - } + public SendTo_SyncSpan(ITestOutputHelper output) : base(output) { } + } - public sealed class SpanForceNonBlocking : SendToBase - { - public SpanForceNonBlocking(ITestOutputHelper output) : base(output) { } - } + public sealed class SendTo_SyncSpanForceNonBlocking : SendToBase + { + public SendTo_SyncSpanForceNonBlocking(ITestOutputHelper output) : base(output) { } + } - public sealed class MemoryArrayTask : SendToBase - { - public MemoryArrayTask(ITestOutputHelper output) : base(output) { } - } + public sealed class SendTo_ArraySync : SendToBase + { + public SendTo_ArraySync(ITestOutputHelper output) : base(output) { } + } - public sealed class MemoryNativeTask : SendToBase - { - public MemoryNativeTask(ITestOutputHelper output) : base(output) { } - } + public sealed class SendTo_SyncForceNonBlocking : SendToBase + { + public SendTo_SyncForceNonBlocking(ITestOutputHelper output) : base(output) {} + } - public sealed class ArraySync : SendToBase - { - public ArraySync(ITestOutputHelper output) : base(output) { } - } + public sealed class SendTo_Apm : SendToBase + { + public SendTo_Apm(ITestOutputHelper output) : base(output) {} + } - public sealed class ArrayForceNonBlocking : SendToBase - { - public ArrayForceNonBlocking(ITestOutputHelper output) : base(output) {} - } - } + public sealed class SendTo_Eap : SendToBase + { + public SendTo_Eap(ITestOutputHelper output) : base(output) {} + } - public static class Async - { - public sealed class Apm : SendToBase - { - public Apm(ITestOutputHelper output) : base(output) {} - } + public sealed class SendTo_Task : SendToBase + { + public SendTo_Task(ITestOutputHelper output) : base(output) { } + } - public sealed class Task : SendToBase - { - public Task(ITestOutputHelper output) : base(output) {} - } + public sealed class SendTo_CancellableTask : SendToBase + { + public SendTo_CancellableTask(ITestOutputHelper output) : base(output) { } + } - public sealed class Eap : SendToBase - { - public Eap(ITestOutputHelper output) : base(output) {} - } - } + public sealed class SendTo_MemoryArrayTask : SendToBase + { + public SendTo_MemoryArrayTask(ITestOutputHelper output) : base(output) { } + } + + public sealed class SendTo_MemoryNativeTask : SendToBase + { + public SendTo_MemoryNativeTask(ITestOutputHelper output) : base(output) { } } } From 77d8832fd6479afb934ea7dbf189a2ad7d01571f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 15 Jan 2021 19:25:01 +0100 Subject: [PATCH 14/42] TestTimes --- .../tests/FunctionalTests/SendTo.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index ec5d3e6463ffb..fd01e069615bf 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -1,6 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -120,6 +123,39 @@ public SendTo_Task(ITestOutputHelper output) : base(output) { } public sealed class SendTo_CancellableTask : SendToBase { public SendTo_CancellableTask(ITestOutputHelper output) : base(output) { } + + [Theory] + [InlineData(10)] + [InlineData(100)] + [InlineData(1000)] + [InlineData(10000)] + public async Task TestTimes(int DatagramCount) + { + //const int DatagramCount = 100; + const int DatagramSize = 2048; + + List sendOps = new List(); + + using Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + sender.BindToAnonymousPort(IPAddress.Loopback); + + using Socket receiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + receiver.BindToAnonymousPort(IPAddress.Loopback); + IPEndPoint remoteEp = (IPEndPoint)receiver.LocalEndPoint; + + Stopwatch sw = new Stopwatch(); + sw.Start(); + + for (int i = 0; i < DatagramCount; i++) + { + ReadOnlyMemory buffer = new byte[DatagramSize]; + Task sendOp = sender.SendToAsync(buffer, SocketFlags.None, remoteEp, default).AsTask(); + } + + await Task.WhenAll(sendOps); + sw.Stop(); + throw new Exception("Total time: " + sw.Elapsed); + } } public sealed class SendTo_MemoryArrayTask : SendToBase From 1328beed29da3f3c1232bcca3d64019c03df9f5b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 19 Jan 2021 15:56:28 +0100 Subject: [PATCH 15/42] SendTo cancellation tests --- .../tests/FunctionalTests/SendTo.cs | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index fd01e069615bf..0b4f0424fa1db 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -12,7 +13,7 @@ namespace System.Net.Sockets.Tests { public abstract class SendToBase : SocketTestHelperBase where T : SocketHelperBase, new() { - private static readonly IPEndPoint ValidUdpRemoteEndpoint = new IPEndPoint(IPAddress.Parse("10.20.30.40"), 1234); + protected static readonly IPEndPoint ValidUdpRemoteEndpoint = new IPEndPoint(IPAddress.Parse("10.20.30.40"), 1234); protected SendToBase(ITestOutputHelper output) : base(output) { @@ -124,37 +125,41 @@ public sealed class SendTo_CancellableTask : SendToBase sendOps = new List(); + OperationCanceledException ex = await Assert.ThrowsAnyAsync( + () => sender.SendToAsync(new byte[1], SocketFlags.None, ValidUdpRemoteEndpoint, cts.Token).AsTask()); - using Socket sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - sender.BindToAnonymousPort(IPAddress.Loopback); + Assert.Equal(cts.Token, ex.CancellationToken); + } + + [PlatformSpecific(TestPlatforms.AnyUnix)] + [Fact] + public async Task CancelDuringOperation_Throws() + { + const int DatagramCount = 50; + const int DatagramSize = 32768; + TimeSpan cancelAfter = TimeSpan.FromMilliseconds(5); - using Socket receiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - receiver.BindToAnonymousPort(IPAddress.Loopback); - IPEndPoint remoteEp = (IPEndPoint)receiver.LocalEndPoint; + using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + int port = client.BindToAnonymousPort(IPAddress.Any); - Stopwatch sw = new Stopwatch(); - sw.Start(); + List tasks = new List(); + CancellationTokenSource cts = new CancellationTokenSource(); for (int i = 0; i < DatagramCount; i++) { - ReadOnlyMemory buffer = new byte[DatagramSize]; - Task sendOp = sender.SendToAsync(buffer, SocketFlags.None, remoteEp, default).AsTask(); + var leftTask = client.SendToAsync(new byte[DatagramSize], SocketFlags.None, ValidUdpRemoteEndpoint, cts.Token); + tasks.Add(leftTask.AsTask()); } + cts.CancelAfter(cancelAfter); - await Task.WhenAll(sendOps); - sw.Stop(); - throw new Exception("Total time: " + sw.Elapsed); + await Assert.ThrowsAnyAsync(() => Task.WhenAll(tasks)); } } From 31e1168a1d1fba3f03d8faba18e882f1df9bca23 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 19 Jan 2021 16:01:24 +0100 Subject: [PATCH 16/42] ReceiveMessageFrom cancellation test --- .../FunctionalTests/ReceiveMessageFrom.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index de3ee73248b7f..3b680c6086655 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -77,6 +77,27 @@ public ReceiveMessageFrom_Task(ITestOutputHelper output) : base(output) { } public sealed class ReceiveMessageFrom_CancellableTask : ReceiveMessageFrom { public ReceiveMessageFrom_CancellableTask(ITestOutputHelper output) : base(output) { } + + [Theory] + [MemberData(nameof(LoopbacksAndBuffers))] + public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) + { + using var socket = new Socket(loopback.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + socket.BindToAnonymousPort(loopback); + Memory buffer = new byte[1]; + + CancellationTokenSource cts = new CancellationTokenSource(); + if (precanceled) cts.Cancel(); + else cts.CancelAfter(10); + + OperationCanceledException ex = await Assert.ThrowsAnyAsync( + () => socket.ReceiveMessageFromAsync(buffer, SocketFlags.None, GetGetDummyTestEndpoint(loopback.AddressFamily), cts.Token).AsTask()); + Assert.Equal(cts.Token, ex.CancellationToken); + + IPEndPoint GetGetDummyTestEndpoint(AddressFamily addressFamily = AddressFamily.InterNetwork) => + addressFamily == AddressFamily.InterNetwork ? + new IPEndPoint(IPAddress.Parse("1.2.3.4"), 1234) : new IPEndPoint(IPAddress.Parse("1:2:3::4"), 1234); + } } public sealed class ReceiveMessageFrom_Eap : ReceiveMessageFrom From 295b3d2499a0a2f0870ee8faff8212c73be13825 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 19 Jan 2021 17:52:28 +0100 Subject: [PATCH 17/42] SendToAsync: handle pre-cancellation --- .../src/System/Net/Sockets/Socket.Tasks.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index 11dd7860087e9..84dc69a17d025 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -514,6 +514,11 @@ internal ValueTask SendToAsync(ReadOnlyMemory buffer, SocketFlags soc throw new ArgumentNullException(nameof(remoteEP)); } + if (cancellationToken.IsCancellationRequested) + { + return ValueTask.FromCanceled(cancellationToken); + } + AwaitableSocketAsyncEventArgs saea = Interlocked.Exchange(ref _singleBufferSendEventArgs, null) ?? new AwaitableSocketAsyncEventArgs(this, isReceiveForCaching: false); From 17a255f35e1390cf14350f6871167dfb5c06cc8c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 19 Jan 2021 17:54:53 +0100 Subject: [PATCH 18/42] Disposed_Throws --- .../tests/FunctionalTests/ReceiveFrom.cs | 10 ++++++++++ .../tests/FunctionalTests/ReceiveMessageFrom.cs | 10 ++++++++++ .../System.Net.Sockets/tests/FunctionalTests/SendTo.cs | 9 +++++++++ 3 files changed, 29 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 13541876098e7..7ee4bec0efbef 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -51,6 +51,16 @@ public async Task NullEndpoint_Throws() await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, new byte[1], null)); } + + [Fact] + public async Task Disposed_Throws() + { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.BindToAnonymousPort(IPAddress.Any); + socket.Dispose(); + + await Assert.ThrowsAsync(() => ReceiveFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())); + } } public sealed class ReceiveFrom_Sync : ReceiveFrom diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index 3b680c6086655..3a31251caa09e 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -52,6 +52,16 @@ public async Task ReceiveSentMessages_Success(bool ipv4) Assert.Equal(((IPEndPoint)sender.LocalEndPoint).Address, packetInformation.Address); } } + + [Fact] + public async Task Disposed_Throws() + { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.BindToAnonymousPort(IPAddress.Any); + socket.Dispose(); + + await Assert.ThrowsAsync(() => ReceiveMessageFromAsync(socket, new byte[1], new IPEndPoint(IPAddress.Parse("1.2.3.4"), 1234))); + } } public sealed class ReceiveMessageFrom_Sync : ReceiveMessageFrom diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index 0b4f0424fa1db..5d01ed45ba800 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -84,6 +84,15 @@ public async Task Datagram_UDP_AccessDenied_Throws_DoesNotBind() Assert.Equal(SocketError.AccessDenied, e.SocketErrorCode); Assert.Null(socket.LocalEndPoint); } + + [Fact] + public async Task Disposed_Throws() + { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.Dispose(); + + await Assert.ThrowsAsync(() => SendToAsync(socket, new byte[1], ValidUdpRemoteEndpoint)); + } } public sealed class SendTo_SyncSpan : SendToBase From f39c94f5503462160076bcea83cc85a35ff2b09b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 19 Jan 2021 19:10:31 +0100 Subject: [PATCH 19/42] move new methods to Socket class --- .../System.Net.Sockets/ref/System.Net.Sockets.cs | 6 +++--- .../src/System/Net/Sockets/Socket.Tasks.cs | 6 +++--- .../src/System/Net/Sockets/SocketTaskExtensions.cs | 9 --------- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs index f74a45e188817..24e3acce9d0e2 100644 --- a/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs +++ b/src/libraries/System.Net.Sockets/ref/System.Net.Sockets.cs @@ -381,9 +381,11 @@ public void Listen(int backlog) { } public int ReceiveFrom(byte[] buffer, ref System.Net.EndPoint remoteEP) { throw null; } public int ReceiveFrom(byte[] buffer, System.Net.Sockets.SocketFlags socketFlags, ref System.Net.EndPoint remoteEP) { throw null; } public System.Threading.Tasks.Task ReceiveFromAsync(System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint) { throw null; } + public System.Threading.Tasks.ValueTask ReceiveFromAsync(System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public bool ReceiveFromAsync(System.Net.Sockets.SocketAsyncEventArgs e) { throw null; } public int ReceiveMessageFrom(byte[] buffer, int offset, int size, ref System.Net.Sockets.SocketFlags socketFlags, ref System.Net.EndPoint remoteEP, out System.Net.Sockets.IPPacketInformation ipPacketInformation) { throw null; } public System.Threading.Tasks.Task ReceiveMessageFromAsync(System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint) { throw null; } + public System.Threading.Tasks.ValueTask ReceiveMessageFromAsync(System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint, System.Threading.CancellationToken cancellationToken = default) { throw null; } public bool ReceiveMessageFromAsync(System.Net.Sockets.SocketAsyncEventArgs e) { throw null; } public static void Select(System.Collections.IList? checkRead, System.Collections.IList? checkWrite, System.Collections.IList? checkError, int microSeconds) { } public int Send(byte[] buffer) { throw null; } @@ -409,6 +411,7 @@ public void SendFile(string? fileName, byte[]? preBuffer, byte[]? postBuffer, Sy public int SendTo(byte[] buffer, System.Net.EndPoint remoteEP) { throw null; } public int SendTo(byte[] buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; } public System.Threading.Tasks.Task SendToAsync(System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; } + public System.Threading.Tasks.ValueTask SendToAsync(System.ReadOnlyMemory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public bool SendToAsync(System.Net.Sockets.SocketAsyncEventArgs e) { throw null; } [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] public void SetIPProtectionLevel(System.Net.Sockets.IPProtectionLevel level) { } @@ -599,10 +602,8 @@ public static partial class SocketTaskExtensions public static System.Threading.Tasks.ValueTask ReceiveAsync(this System.Net.Sockets.Socket socket, System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static System.Threading.Tasks.Task ReceiveFromAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint) { throw null; } - public static System.Threading.Tasks.ValueTask ReceiveFromAsync(this System.Net.Sockets.Socket socket, System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static System.Threading.Tasks.Task ReceiveMessageFromAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint) { throw null; } - public static System.Threading.Tasks.ValueTask ReceiveMessageFromAsync(this System.Net.Sockets.Socket socket, System.Memory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEndPoint, System.Threading.CancellationToken cancellationToken = default) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static System.Threading.Tasks.Task SendAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -611,7 +612,6 @@ public static partial class SocketTaskExtensions public static System.Threading.Tasks.ValueTask SendAsync(this System.Net.Sockets.Socket socket, System.ReadOnlyMemory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public static System.Threading.Tasks.Task SendToAsync(this System.Net.Sockets.Socket socket, System.ArraySegment buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) { throw null; } - public static System.Threading.Tasks.ValueTask SendToAsync(this System.Net.Sockets.Socket socket, System.ReadOnlyMemory buffer, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } } public enum SocketType { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index 84dc69a17d025..1d3f1c6e2c3de 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -340,7 +340,7 @@ public Task ReceiveFromAsync(ArraySegment buffer, return ReceiveFromAsync(buffer, socketFlags, remoteEndPoint, default).AsTask(); } - internal ValueTask ReceiveFromAsync(Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken) + public ValueTask ReceiveFromAsync(Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) { if (remoteEndPoint is null) { @@ -385,7 +385,7 @@ public Task ReceiveMessageFromAsync(ArraySegment return ReceiveMessageFromAsync(buffer, socketFlags, remoteEndPoint, default).AsTask(); } - internal ValueTask ReceiveMessageFromAsync(Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken) + public ValueTask ReceiveMessageFromAsync(Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) { if (remoteEndPoint is null) { @@ -507,7 +507,7 @@ public Task SendToAsync(ArraySegment buffer, SocketFlags socketFlags, return SendToAsync(buffer, socketFlags, remoteEP, default).AsTask(); } - internal ValueTask SendToAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken) + public ValueTask SendToAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = default) { if (remoteEP is null) { diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs index 52b44143aea9a..5197487c8cbab 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.cs @@ -51,19 +51,12 @@ public static ValueTask ReceiveAsync(this Socket socket, Memory buffe [EditorBrowsable(EditorBrowsableState.Never)] public static Task ReceiveAsync(this Socket socket, IList> buffers, SocketFlags socketFlags) => socket.ReceiveAsync(buffers, socketFlags); - [EditorBrowsable(EditorBrowsableState.Never)] public static Task ReceiveFromAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEndPoint) => socket.ReceiveFromAsync(buffer, socketFlags, remoteEndPoint); [EditorBrowsable(EditorBrowsableState.Never)] - public static ValueTask ReceiveFromAsync(this Socket socket, Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) => - socket.ReceiveFromAsync(buffer, socketFlags, remoteEndPoint, cancellationToken); - public static Task ReceiveMessageFromAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEndPoint) => socket.ReceiveMessageFromAsync(buffer, socketFlags, remoteEndPoint); - public static ValueTask ReceiveMessageFromAsync(this Socket socket, Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) => - socket.ReceiveMessageFromAsync(buffer, socketFlags, remoteEndPoint, cancellationToken); - [EditorBrowsable(EditorBrowsableState.Never)] public static Task SendAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags) => @@ -78,7 +71,5 @@ public static Task SendAsync(this Socket socket, IList> [EditorBrowsable(EditorBrowsableState.Never)] public static Task SendToAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEP) => socket.SendToAsync(buffer, socketFlags, remoteEP); - public static ValueTask SendToAsync(this Socket socket, ReadOnlyMemory buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = default) => - socket.SendToAsync(buffer, socketFlags, remoteEP, cancellationToken); } } From 3ba53c127bcb87ee6fbbcae1ffd6ee68fb7f428d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 19 Jan 2021 19:18:44 +0100 Subject: [PATCH 20/42] add xmdoc comments --- .../src/System/Net/Sockets/Socket.Tasks.cs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index 1d3f1c6e2c3de..ca6c079bf0c2e 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -333,13 +333,21 @@ public Task ReceiveAsync(IList> buffers, SocketFlags soc /// The buffer for the received data. /// A bitwise combination of SocketFlags values that will be used when receiving the data. /// An endpoint of the same type as the endpoint of the remote host. - /// An asynchronous task that completes with a SocketReceiveFromResult containing the number of bytes received and the endpoint of the sending host. + /// An asynchronous task that completes with a containing the number of bytes received and the endpoint of the sending host. public Task ReceiveFromAsync(ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEndPoint) { ValidateBuffer(buffer); return ReceiveFromAsync(buffer, socketFlags, remoteEndPoint, default).AsTask(); } + /// + /// Receives data and returns the endpoint of the sending host. + /// + /// The buffer for the received data. + /// A bitwise combination of SocketFlags values that will be used when receiving the data. + /// An endpoint of the same type as the endpoint of the remote host. + /// A cancellation token that can be used to signal the asynchronous operation should be canceled. + /// An asynchronous task that completes with a containing the number of bytes received and the endpoint of the sending host. public ValueTask ReceiveFromAsync(Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) { if (remoteEndPoint is null) @@ -378,13 +386,21 @@ public ValueTask ReceiveFromAsync(Memory buffer, /// The buffer for the received data. /// A bitwise combination of SocketFlags values that will be used when receiving the data. /// An endpoint of the same type as the endpoint of the remote host. - /// An asynchronous task that completes with a SocketReceiveMessageFromResult containing the number of bytes received and additional information about the sending host. + /// An asynchronous task that completes with a containing the number of bytes received and additional information about the sending host. public Task ReceiveMessageFromAsync(ArraySegment buffer, SocketFlags socketFlags, EndPoint remoteEndPoint) { ValidateBuffer(buffer); return ReceiveMessageFromAsync(buffer, socketFlags, remoteEndPoint, default).AsTask(); } + /// + /// Receives data and returns additional information about the sender of the message. + /// + /// The buffer for the received data. + /// A bitwise combination of SocketFlags values that will be used when receiving the data. + /// An endpoint of the same type as the endpoint of the remote host. + /// A cancellation token that can be used to signal the asynchronous operation should be canceled. + /// An asynchronous task that completes with a containing the number of bytes received and additional information about the sending host. public ValueTask ReceiveMessageFromAsync(Memory buffer, SocketFlags socketFlags, EndPoint remoteEndPoint, CancellationToken cancellationToken = default) { if (remoteEndPoint is null) @@ -507,6 +523,14 @@ public Task SendToAsync(ArraySegment buffer, SocketFlags socketFlags, return SendToAsync(buffer, socketFlags, remoteEP, default).AsTask(); } + /// + /// Sends data to the specified remote host. + /// + /// The buffer for the data to send. + /// A bitwise combination of SocketFlags values that will be used when sending the data. + /// The remote host to which to send the data. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// An asynchronous task that completes with the number of bytes sent. public ValueTask SendToAsync(ReadOnlyMemory buffer, SocketFlags socketFlags, EndPoint remoteEP, CancellationToken cancellationToken = default) { if (remoteEP is null) From aa1bf4266fccc8afc953be16edbb2bab69da4be3 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 19 Jan 2021 19:23:36 +0100 Subject: [PATCH 21/42] improve reliability of cancellation tests --- .../System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs | 5 +++-- .../tests/FunctionalTests/ReceiveMessageFrom.cs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 7ee4bec0efbef..3fbf379feabc1 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -97,10 +97,11 @@ public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) CancellationTokenSource cts = new CancellationTokenSource(); if (precanceled) cts.Cancel(); - else cts.CancelAfter(10); + else cts.CancelAfter(100); OperationCanceledException ex = await Assert.ThrowsAnyAsync( - () => socket.ReceiveFromAsync(buffer, SocketFlags.None, GetGetDummyTestEndpoint(loopback.AddressFamily), cts.Token).AsTask()); + () => socket.ReceiveFromAsync(buffer, SocketFlags.None, GetGetDummyTestEndpoint(loopback.AddressFamily), cts.Token).AsTask()) + .TimeoutAfter(10_000); Assert.Equal(cts.Token, ex.CancellationToken); } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index 3a31251caa09e..e683c68b86b61 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -98,10 +98,11 @@ public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) CancellationTokenSource cts = new CancellationTokenSource(); if (precanceled) cts.Cancel(); - else cts.CancelAfter(10); + else cts.CancelAfter(100); OperationCanceledException ex = await Assert.ThrowsAnyAsync( - () => socket.ReceiveMessageFromAsync(buffer, SocketFlags.None, GetGetDummyTestEndpoint(loopback.AddressFamily), cts.Token).AsTask()); + () => socket.ReceiveMessageFromAsync(buffer, SocketFlags.None, GetGetDummyTestEndpoint(loopback.AddressFamily), cts.Token).AsTask()) + .TimeoutAfter(10_000); Assert.Equal(cts.Token, ex.CancellationToken); IPEndPoint GetGetDummyTestEndpoint(AddressFamily addressFamily = AddressFamily.InterNetwork) => From 4e4add1dabedfedd45a53e1060ecc13e2226220f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 19 Jan 2021 20:38:18 +0100 Subject: [PATCH 22/42] fix ReceiveMessageFrom cancellation on Windows --- .../src/System/Net/Sockets/Socket.Tasks.cs | 1 - .../Sockets/SocketAsyncEventArgs.Windows.cs | 62 +++++++++++-------- .../tests/FunctionalTests/ReceiveFrom.cs | 4 +- .../FunctionalTests/ReceiveMessageFrom.cs | 27 ++++---- 4 files changed, 54 insertions(+), 40 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index ca6c079bf0c2e..b18daa269af0f 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -415,7 +415,6 @@ public ValueTask ReceiveMessageFromAsync(Memory< { throw new InvalidOperationException(SR.net_sockets_mustbind); } - if (cancellationToken.IsCancellationRequested) { return ValueTask.FromCanceled(cancellationToken); diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs index f70030483b442..3b5b02a6cf77c 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs @@ -558,25 +558,33 @@ internal unsafe SocketError DoOperationReceiveMessageFrom(Socket socket, SafeSoc _wsaRecvMsgWSABufferArrayPinned = GC.AllocateUninitializedArray(1, pinned: true); } - Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None); - _singleBufferHandle = _buffer.Pin(); - _singleBufferHandleState = SingleBufferHandleState.Set; + fixed (byte* bufferPtr = &MemoryMarshal.GetReference(_buffer.Span)) + { + Debug.Assert(_singleBufferHandleState == SingleBufferHandleState.None); + _singleBufferHandleState = SingleBufferHandleState.InProcess; + + _wsaRecvMsgWSABufferArrayPinned[0].Pointer = (IntPtr)bufferPtr; + _wsaRecvMsgWSABufferArrayPinned[0].Length = _count; + wsaRecvMsgWSABufferArray = _wsaRecvMsgWSABufferArrayPinned; + wsaRecvMsgWSABufferCount = 1; - _wsaRecvMsgWSABufferArrayPinned[0].Pointer = (IntPtr)_singleBufferHandle.Pointer; - _wsaRecvMsgWSABufferArrayPinned[0].Length = _count; - wsaRecvMsgWSABufferArray = _wsaRecvMsgWSABufferArrayPinned; - wsaRecvMsgWSABufferCount = 1; + return Core(); + } } else { // Use the multi-buffer WSABuffer. wsaRecvMsgWSABufferArray = _wsaBufferArrayPinned!; wsaRecvMsgWSABufferCount = (uint)_bufferListInternal!.Count; + + return Core(); } - // Fill in WSAMessageBuffer. - unsafe + // Fill in WSAMessageBuffer, run WSARecvMsg and process the IOCP result. + // Logic is in a separate method so we can share code between the (pinned) single buffer and the multi-buffer case + SocketError Core() { + // Fill in WSAMessageBuffer. Interop.Winsock.WSAMsg* pMessage = (Interop.Winsock.WSAMsg*)Marshal.UnsafeAddrOfPinnedArrayElement(_wsaMessageBufferPinned, 0); pMessage->socketAddress = PtrSocketAddressBuffer; pMessage->addressLength = (uint)_socketAddress.Size; @@ -596,26 +604,26 @@ internal unsafe SocketError DoOperationReceiveMessageFrom(Socket socket, SafeSoc pMessage->controlBuffer.Length = _controlBufferPinned.Length; } pMessage->flags = _socketFlags; - } - NativeOverlapped* overlapped = AllocateNativeOverlapped(); - try - { - SocketError socketError = socket.WSARecvMsg( - handle, - Marshal.UnsafeAddrOfPinnedArrayElement(_wsaMessageBufferPinned, 0), - out int bytesTransferred, - overlapped, - IntPtr.Zero); + NativeOverlapped* overlapped = AllocateNativeOverlapped(); + try + { + SocketError socketError = socket.WSARecvMsg( + handle, + Marshal.UnsafeAddrOfPinnedArrayElement(_wsaMessageBufferPinned, 0), + out int bytesTransferred, + overlapped, + IntPtr.Zero); - return ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred, overlapped, cancellationToken); - } - catch - { - _singleBufferHandleState = SingleBufferHandleState.None; - FreeNativeOverlapped(overlapped); - _singleBufferHandle.Dispose(); - throw; + return ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred, overlapped, cancellationToken); + } + catch + { + _singleBufferHandleState = SingleBufferHandleState.None; + FreeNativeOverlapped(overlapped); + _singleBufferHandle.Dispose(); + throw; + } } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 3fbf379feabc1..e889851759060 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -92,7 +92,9 @@ public ReceiveFrom_CancellableTask(ITestOutputHelper output) : base(output) { } public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) { using var socket = new Socket(loopback.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + using var dummy = new Socket(loopback.AddressFamily, SocketType.Dgram, ProtocolType.Udp); socket.BindToAnonymousPort(loopback); + dummy.BindToAnonymousPort(loopback); Memory buffer = new byte[1]; CancellationTokenSource cts = new CancellationTokenSource(); @@ -100,7 +102,7 @@ public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) else cts.CancelAfter(100); OperationCanceledException ex = await Assert.ThrowsAnyAsync( - () => socket.ReceiveFromAsync(buffer, SocketFlags.None, GetGetDummyTestEndpoint(loopback.AddressFamily), cts.Token).AsTask()) + () => socket.ReceiveFromAsync(buffer, SocketFlags.None, dummy.LocalEndPoint, cts.Token).AsTask()) .TimeoutAfter(10_000); Assert.Equal(cts.Token, ex.CancellationToken); } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index e683c68b86b61..db8f78815a6f1 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Xunit; @@ -93,7 +94,9 @@ public ReceiveMessageFrom_CancellableTask(ITestOutputHelper output) : base(outpu public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) { using var socket = new Socket(loopback.AddressFamily, SocketType.Dgram, ProtocolType.Udp); + using var dummy = new Socket(loopback.AddressFamily, SocketType.Dgram, ProtocolType.Udp); socket.BindToAnonymousPort(loopback); + dummy.BindToAnonymousPort(loopback); Memory buffer = new byte[1]; CancellationTokenSource cts = new CancellationTokenSource(); @@ -101,13 +104,9 @@ public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) else cts.CancelAfter(100); OperationCanceledException ex = await Assert.ThrowsAnyAsync( - () => socket.ReceiveMessageFromAsync(buffer, SocketFlags.None, GetGetDummyTestEndpoint(loopback.AddressFamily), cts.Token).AsTask()) + () => socket.ReceiveMessageFromAsync(buffer, SocketFlags.None, dummy.LocalEndPoint, cts.Token).AsTask()) .TimeoutAfter(10_000); Assert.Equal(cts.Token, ex.CancellationToken); - - IPEndPoint GetGetDummyTestEndpoint(AddressFamily addressFamily = AddressFamily.InterNetwork) => - addressFamily == AddressFamily.InterNetwork ? - new IPEndPoint(IPAddress.Parse("1.2.3.4"), 1234) : new IPEndPoint(IPAddress.Parse("1:2:3::4"), 1234); } } @@ -124,7 +123,7 @@ public ReceiveMessageFrom_Eap(ITestOutputHelper output) : base(output) { } [InlineData(true, 2)] public void ReceiveSentMessages_ReuseEventArgs_Success(bool ipv4, int bufferMode) { - const int DatagramsToSend = 30; + const int DatagramsToSend = 5; const int TimeoutMs = 30_000; AddressFamily family; @@ -156,34 +155,40 @@ public void ReceiveSentMessages_ReuseEventArgs_Success(bool ipv4, int bufferMode sender.Bind(new IPEndPoint(loopback, 0)); saea.RemoteEndPoint = new IPEndPoint(any, 0); + Random random = new Random(0); + byte[] sendBuffer = new byte[1024]; + random.NextBytes(sendBuffer); + for (int i = 0; i < DatagramsToSend; i++) { + byte[] receiveBuffer = new byte[1024]; switch (bufferMode) { case 0: // single buffer - saea.SetBuffer(new byte[1024], 0, 1024); + saea.SetBuffer(receiveBuffer, 0, 1024); break; case 1: // single buffer in buffer list saea.BufferList = new List> { - new ArraySegment(new byte[1024]) + new ArraySegment(receiveBuffer) }; break; case 2: // multiple buffers in buffer list saea.BufferList = new List> { - new ArraySegment(new byte[512]), - new ArraySegment(new byte[512]) + new ArraySegment(receiveBuffer, 0, 512), + new ArraySegment(receiveBuffer, 512, 512) }; break; } bool pending = receiver.ReceiveMessageFromAsync(saea); - sender.SendTo(new byte[1024], new IPEndPoint(loopback, port)); + sender.SendTo(sendBuffer, new IPEndPoint(loopback, port)); if (pending) Assert.True(completed.Wait(TimeoutMs), "Expected operation to complete within timeout"); completed.Reset(); Assert.Equal(1024, saea.BytesTransferred); + AssertExtensions.SequenceEqual(sendBuffer, receiveBuffer); Assert.Equal(sender.LocalEndPoint, saea.RemoteEndPoint); Assert.Equal(((IPEndPoint)sender.LocalEndPoint).Address, saea.ReceiveMessageFromPacketInfo.Address); } From 42d0cca43e02230893c4d41df08fac6873879ee4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 19 Jan 2021 21:13:45 +0100 Subject: [PATCH 23/42] add comments to SendTo cancellation test --- .../System.Net.Sockets/tests/FunctionalTests/SendTo.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index 5d01ed45ba800..88eba7c80ecee 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -147,6 +147,8 @@ public async Task PreCanceled_Throws() Assert.Equal(cts.Token, ex.CancellationToken); } + // On Unix, flooding the kernel with sendto calls will lead to actual asynchronous completions, which allows us to test cancellation. + // On Windows, WSASendTo/WSASendMsg are currently not cancellable. [PlatformSpecific(TestPlatforms.AnyUnix)] [Fact] public async Task CancelDuringOperation_Throws() @@ -161,6 +163,8 @@ public async Task CancelDuringOperation_Throws() List tasks = new List(); CancellationTokenSource cts = new CancellationTokenSource(); + // After flooding the socket with a high number of send tasks, + // we assume some of them won't complete before the "CancelAfter" period expires. for (int i = 0; i < DatagramCount; i++) { var leftTask = client.SendToAsync(new byte[DatagramSize], SocketFlags.None, ValidUdpRemoteEndpoint, cts.Token); From aa08745274aece59ae4e06d000543c312a500dd1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 19 Jan 2021 22:11:08 +0100 Subject: [PATCH 24/42] update comments --- .../System.Net.Sockets/tests/FunctionalTests/SendTo.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index 88eba7c80ecee..16f85a3fa5288 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -147,8 +147,10 @@ public async Task PreCanceled_Throws() Assert.Equal(cts.Token, ex.CancellationToken); } - // On Unix, flooding the kernel with sendto calls will lead to actual asynchronous completions, which allows us to test cancellation. - // On Windows, WSASendTo/WSASendMsg are currently not cancellable. + // On Unix, flooding the kernel with sendto calls will lead to actual asynchronous completions, + // so we can test the cancellation logic implemented in SocketAsyncContext. + // On Windows, WSASendTo/WSASendMsg implementations are not cancellable with CancelIoEx as of 01/2021, + // which means CancellationToken will only take effect if it's precancelled. This may change in the future. [PlatformSpecific(TestPlatforms.AnyUnix)] [Fact] public async Task CancelDuringOperation_Throws() From 19f3efca5b862f17f7c7649821360f16e6a290a9 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 21 Jan 2021 12:03:06 +0100 Subject: [PATCH 25/42] Fix single/multibuffer handling in DoOperationReceiveMessageFrom --- .../src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs index 3b5b02a6cf77c..25804b7af8d35 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs @@ -615,13 +615,14 @@ SocketError Core() overlapped, IntPtr.Zero); - return ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred, overlapped, cancellationToken); + return _bufferList == null ? + ProcessIOCPResultWithSingleBufferHandle(socketError, bytesTransferred, overlapped, cancellationToken) : + ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred, overlapped); } catch { _singleBufferHandleState = SingleBufferHandleState.None; FreeNativeOverlapped(overlapped); - _singleBufferHandle.Dispose(); throw; } } From 529847567a882a42749b70c57c057b535baa8159 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 21 Jan 2021 15:08:20 +0100 Subject: [PATCH 26/42] ReceiveFrom: tests for shutdown/close/dispose cases --- .../tests/FunctionalTests/ReceiveFrom.cs | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index e889851759060..672a60226d8f1 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -52,15 +52,74 @@ public async Task NullEndpoint_Throws() await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, new byte[1], null)); } - [Fact] - public async Task Disposed_Throws() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ClosedBeforeOperation_Throws_ObjectDisposedException(bool closeOrDispose) { using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.BindToAnonymousPort(IPAddress.Any); - socket.Dispose(); + if (closeOrDispose) socket.Close(); + else socket.Dispose(); await Assert.ThrowsAsync(() => ReceiveFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())); } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ClosedDuringOperation_Throws_ObjectDisposedExceptionOrSocketException(bool closeOrDispose) + { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.BindToAnonymousPort(IPAddress.Any); + + Task receiveTask = ReceiveFromAsync(socket, new byte[1], GetGetDummyTestEndpoint()); + await Task.Delay(100); + if (closeOrDispose) socket.Close(); + else socket.Dispose(); + + if (UsesApm) + { + await Assert.ThrowsAsync(() => receiveTask); + } + else + { + SocketException ex = await Assert.ThrowsAsync(() => receiveTask); + SocketError expectedError = UsesSync ? SocketError.Interrupted : SocketError.OperationAborted; + Assert.Equal(expectedError, ex.SocketErrorCode); + } + } + + [Theory] + [InlineData(SocketShutdown.Both)] + [InlineData(SocketShutdown.Receive)] + public async Task ShutdownReceiveBeforeOperation_ThrowsSocketException(SocketShutdown shutdown) + { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.BindToAnonymousPort(IPAddress.Any); + socket.Shutdown(shutdown); + + SocketException exception = await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())) + .TimeoutAfter(10_000); + + Assert.Equal(SocketError.Shutdown, exception.SocketErrorCode); + _output.WriteLine($"{exception.GetType()} -- {exception.Message}"); + } + + [Fact] + public async Task ShutdownSend_ReceiveFromShouldSucceed() + { + using var receiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + receiver.BindToAnonymousPort(IPAddress.Loopback); + receiver.Shutdown(SocketShutdown.Send); + + using var sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + sender.BindToAnonymousPort(IPAddress.Loopback); + sender.SendTo(new byte[1], receiver.LocalEndPoint); + + var r = await ReceiveFromAsync(receiver, new byte[1], sender.LocalEndPoint); + Assert.Equal(1, r.ReceivedBytes); + } } public sealed class ReceiveFrom_Sync : ReceiveFrom From 85915a34283b1021c5e1210716d64ba676e629a8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 21 Jan 2021 15:31:00 +0100 Subject: [PATCH 27/42] Use TCP for shutdown() tests --- .../tests/FunctionalTests/ReceiveFrom.cs | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 672a60226d8f1..d32a25e2feae7 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -95,30 +95,34 @@ public async Task ClosedDuringOperation_Throws_ObjectDisposedExceptionOrSocketEx [InlineData(SocketShutdown.Receive)] public async Task ShutdownReceiveBeforeOperation_ThrowsSocketException(SocketShutdown shutdown) { - using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - socket.BindToAnonymousPort(IPAddress.Any); - socket.Shutdown(shutdown); - - SocketException exception = await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())) - .TimeoutAfter(10_000); + // It's allowed to shutdown() UDP sockets on Windows, however on Unix it will lead to ENOTCONN + // Using TCP for the sake of this test. + (Socket receiver, Socket sender) = SocketTestExtensions.CreateConnectedSocketPair(); + using (receiver) + using (sender) + { + receiver.Shutdown(shutdown); + SocketException exception = await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(receiver, new byte[1], sender.LocalEndPoint)) + .TimeoutAfter(10_000); - Assert.Equal(SocketError.Shutdown, exception.SocketErrorCode); - _output.WriteLine($"{exception.GetType()} -- {exception.Message}"); + Assert.Equal(SocketError.Shutdown, exception.SocketErrorCode); + } } [Fact] public async Task ShutdownSend_ReceiveFromShouldSucceed() { - using var receiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - receiver.BindToAnonymousPort(IPAddress.Loopback); - receiver.Shutdown(SocketShutdown.Send); - - using var sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - sender.BindToAnonymousPort(IPAddress.Loopback); - sender.SendTo(new byte[1], receiver.LocalEndPoint); - - var r = await ReceiveFromAsync(receiver, new byte[1], sender.LocalEndPoint); - Assert.Equal(1, r.ReceivedBytes); + // It's allowed to shutdown() UDP sockets on Windows, however on Unix it will lead to ENOTCONN + // Using TCP for the sake of this test. + (Socket receiver, Socket sender) = SocketTestExtensions.CreateConnectedSocketPair(); + using (receiver) + using (sender) + { + receiver.Shutdown(SocketShutdown.Send); + sender.Send(new byte[1]); + var r = await ReceiveFromAsync(receiver, new byte[1], sender.LocalEndPoint); + Assert.Equal(1, r.ReceivedBytes); + } } } From ecd096685ff5bc6ff4078515c39eaf0b7f8faf3c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 21 Jan 2021 15:34:10 +0100 Subject: [PATCH 28/42] Revert "Use TCP for shutdown() tests" This reverts commit 85915a34283b1021c5e1210716d64ba676e629a8. --- .../tests/FunctionalTests/ReceiveFrom.cs | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index d32a25e2feae7..672a60226d8f1 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -95,34 +95,30 @@ public async Task ClosedDuringOperation_Throws_ObjectDisposedExceptionOrSocketEx [InlineData(SocketShutdown.Receive)] public async Task ShutdownReceiveBeforeOperation_ThrowsSocketException(SocketShutdown shutdown) { - // It's allowed to shutdown() UDP sockets on Windows, however on Unix it will lead to ENOTCONN - // Using TCP for the sake of this test. - (Socket receiver, Socket sender) = SocketTestExtensions.CreateConnectedSocketPair(); - using (receiver) - using (sender) - { - receiver.Shutdown(shutdown); - SocketException exception = await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(receiver, new byte[1], sender.LocalEndPoint)) - .TimeoutAfter(10_000); + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.BindToAnonymousPort(IPAddress.Any); + socket.Shutdown(shutdown); - Assert.Equal(SocketError.Shutdown, exception.SocketErrorCode); - } + SocketException exception = await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())) + .TimeoutAfter(10_000); + + Assert.Equal(SocketError.Shutdown, exception.SocketErrorCode); + _output.WriteLine($"{exception.GetType()} -- {exception.Message}"); } [Fact] public async Task ShutdownSend_ReceiveFromShouldSucceed() { - // It's allowed to shutdown() UDP sockets on Windows, however on Unix it will lead to ENOTCONN - // Using TCP for the sake of this test. - (Socket receiver, Socket sender) = SocketTestExtensions.CreateConnectedSocketPair(); - using (receiver) - using (sender) - { - receiver.Shutdown(SocketShutdown.Send); - sender.Send(new byte[1]); - var r = await ReceiveFromAsync(receiver, new byte[1], sender.LocalEndPoint); - Assert.Equal(1, r.ReceivedBytes); - } + using var receiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + receiver.BindToAnonymousPort(IPAddress.Loopback); + receiver.Shutdown(SocketShutdown.Send); + + using var sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + sender.BindToAnonymousPort(IPAddress.Loopback); + sender.SendTo(new byte[1], receiver.LocalEndPoint); + + var r = await ReceiveFromAsync(receiver, new byte[1], sender.LocalEndPoint); + Assert.Equal(1, r.ReceivedBytes); } } From 8bbe135ac0daaf56b1adba26ffe42c027c9f7c6c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 21 Jan 2021 15:37:02 +0100 Subject: [PATCH 29/42] make UDP Shutdown tests [PlatformSpecific] --- .../System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 672a60226d8f1..aac0a9b82d449 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -90,6 +90,7 @@ public async Task ClosedDuringOperation_Throws_ObjectDisposedExceptionOrSocketEx } } + [PlatformSpecific(TestPlatforms.Windows)] // It's allowed to shutdown() UDP sockets on Windows, however on Unix this will lead to ENOTCONN [Theory] [InlineData(SocketShutdown.Both)] [InlineData(SocketShutdown.Receive)] @@ -103,9 +104,9 @@ public async Task ShutdownReceiveBeforeOperation_ThrowsSocketException(SocketShu .TimeoutAfter(10_000); Assert.Equal(SocketError.Shutdown, exception.SocketErrorCode); - _output.WriteLine($"{exception.GetType()} -- {exception.Message}"); } + [PlatformSpecific(TestPlatforms.Windows)] // It's allowed to shutdown() UDP sockets on Windows, however on Unix this will lead to ENOTCONN [Fact] public async Task ShutdownSend_ReceiveFromShouldSucceed() { From 9805646e8b11c54936343c47365c72a6258e401f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 21 Jan 2021 16:17:58 +0100 Subject: [PATCH 30/42] ReceiveMessageFrom: Close/Dispose/Shutdown tests --- .../FunctionalTests/ReceiveMessageFrom.cs | 71 +++++++++++++++++-- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index db8f78815a6f1..77a3b9ede32eb 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -12,6 +12,9 @@ namespace System.Net.Sockets.Tests { public abstract class ReceiveMessageFrom : SocketTestHelperBase where T : SocketHelperBase, new() { + protected static IPEndPoint GetGetDummyTestEndpoint(AddressFamily addressFamily = AddressFamily.InterNetwork) => + addressFamily == AddressFamily.InterNetwork ? new IPEndPoint(IPAddress.Parse("1.2.3.4"), 1234) : new IPEndPoint(IPAddress.Parse("1:2:3::4"), 1234); + protected ReceiveMessageFrom(ITestOutputHelper output) : base(output) { } [Theory] @@ -54,14 +57,74 @@ public async Task ReceiveSentMessages_Success(bool ipv4) } } - [Fact] - public async Task Disposed_Throws() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ClosedBeforeOperation_Throws_ObjectDisposedException(bool closeOrDispose) + { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.BindToAnonymousPort(IPAddress.Any); + if (closeOrDispose) socket.Close(); + else socket.Dispose(); + + await Assert.ThrowsAsync(() => ReceiveMessageFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ClosedDuringOperation_Throws_ObjectDisposedExceptionOrSocketException(bool closeOrDispose) { using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.BindToAnonymousPort(IPAddress.Any); - socket.Dispose(); - await Assert.ThrowsAsync(() => ReceiveMessageFromAsync(socket, new byte[1], new IPEndPoint(IPAddress.Parse("1.2.3.4"), 1234))); + Task receiveTask = ReceiveMessageFromAsync(socket, new byte[1], GetGetDummyTestEndpoint()); + await Task.Delay(100); + if (closeOrDispose) socket.Close(); + else socket.Dispose(); + + if (UsesApm) + { + await Assert.ThrowsAsync(() => receiveTask); + } + else + { + SocketException ex = await Assert.ThrowsAsync(() => receiveTask); + SocketError expectedError = UsesSync ? SocketError.Interrupted : SocketError.OperationAborted; + Assert.Equal(expectedError, ex.SocketErrorCode); + } + } + + [PlatformSpecific(TestPlatforms.Windows)] // It's allowed to shutdown() UDP sockets on Windows, however on Unix this will lead to ENOTCONN + [Theory] + [InlineData(SocketShutdown.Both)] + [InlineData(SocketShutdown.Receive)] + public async Task ShutdownReceiveBeforeOperation_ThrowsSocketException(SocketShutdown shutdown) + { + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + socket.BindToAnonymousPort(IPAddress.Any); + socket.Shutdown(shutdown); + + SocketException exception = await Assert.ThrowsAnyAsync(() => ReceiveMessageFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())) + .TimeoutAfter(10_000); + + Assert.Equal(SocketError.Shutdown, exception.SocketErrorCode); + } + + [PlatformSpecific(TestPlatforms.Windows)] // It's allowed to shutdown() UDP sockets on Windows, however on Unix this will lead to ENOTCONN + [Fact] + public async Task ShutdownSend_ReceiveFromShouldSucceed() + { + using var receiver = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + receiver.BindToAnonymousPort(IPAddress.Loopback); + receiver.Shutdown(SocketShutdown.Send); + + using var sender = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + sender.BindToAnonymousPort(IPAddress.Loopback); + sender.SendTo(new byte[1], receiver.LocalEndPoint); + + var r = await ReceiveMessageFromAsync(receiver, new byte[1], sender.LocalEndPoint); + Assert.Equal(1, r.ReceivedBytes); } } From 9baa8438de27c335a3c2d3c0776d3846045171e1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 21 Jan 2021 16:49:00 +0100 Subject: [PATCH 31/42] TCP tests for ReceiveFrom & ReceiveMessageFrom --- .../tests/FunctionalTests/ReceiveFrom.cs | 19 +++++++++++++++++ .../FunctionalTests/ReceiveMessageFrom.cs | 21 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index aac0a9b82d449..2f56e133e81e0 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -52,6 +52,25 @@ public async Task NullEndpoint_Throws() await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, new byte[1], null)); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ReceiveSent_TCP_Success(bool ipv6) + { + (Socket sender, Socket receiver) = SocketTestExtensions.CreateConnectedSocketPair(ipv6); + using (sender) + using (receiver) + { + byte[] sendBuffer = { 1, 2, 3 }; + sender.Send(sendBuffer); + + byte[] receiveBuffer = new byte[3]; + var r = await ReceiveFromAsync(receiver, receiveBuffer, sender.LocalEndPoint); + Assert.Equal(3, r.ReceivedBytes); + AssertExtensions.SequenceEqual(sendBuffer, receiveBuffer); + } + } + [Theory] [InlineData(true)] [InlineData(false)] diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index 77a3b9ede32eb..90b7f42796195 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -20,7 +20,26 @@ protected ReceiveMessageFrom(ITestOutputHelper output) : base(output) { } [Theory] [InlineData(false)] [InlineData(true)] - public async Task ReceiveSentMessages_Success(bool ipv4) + public async Task ReceiveSent_TCP_Success(bool ipv6) + { + (Socket sender, Socket receiver) = SocketTestExtensions.CreateConnectedSocketPair(ipv6); + using (sender) + using (receiver) + { + byte[] sendBuffer = { 1, 2, 3 }; + sender.Send(sendBuffer); + + byte[] receiveBuffer = new byte[3]; + var r = await ReceiveMessageFromAsync(receiver, receiveBuffer, sender.LocalEndPoint); + Assert.Equal(3, r.ReceivedBytes); + AssertExtensions.SequenceEqual(sendBuffer, receiveBuffer); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ReceiveSentMessages_UDP_Success(bool ipv4) { const int DatagramSize = 256; const int DatagramsToSend = 16; From c5191b0ffa8e9f36ded2db21506f5773e27e78f8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 21 Jan 2021 17:11:37 +0100 Subject: [PATCH 32/42] ReceiveMessageFrom + TCP test should be [PlatformSpecific] --- .../tests/FunctionalTests/ReceiveMessageFrom.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index 90b7f42796195..534557f884d6b 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -17,6 +17,7 @@ protected static IPEndPoint GetGetDummyTestEndpoint(AddressFamily addressFamily protected ReceiveMessageFrom(ITestOutputHelper output) : base(output) { } + [PlatformSpecific(TestPlatforms.AnyUnix)] [Theory] [InlineData(false)] [InlineData(true)] From f3a7396e8577824a2b59a71131406c3c28265af3 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 21 Jan 2021 17:33:09 +0100 Subject: [PATCH 33/42] attempt to stabilize tests --- .../tests/FunctionalTests/ReceiveFrom.cs | 2 +- .../FunctionalTests/ReceiveMessageFrom.cs | 2 +- .../tests/FunctionalTests/SendTo.cs | 36 +++++++++++-------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 2f56e133e81e0..98a3b6b4ca665 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -182,7 +182,7 @@ public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) OperationCanceledException ex = await Assert.ThrowsAnyAsync( () => socket.ReceiveFromAsync(buffer, SocketFlags.None, dummy.LocalEndPoint, cts.Token).AsTask()) - .TimeoutAfter(10_000); + .TimeoutAfter(30_000); Assert.Equal(cts.Token, ex.CancellationToken); } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index 534557f884d6b..1941657a5372f 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -188,7 +188,7 @@ public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) OperationCanceledException ex = await Assert.ThrowsAnyAsync( () => socket.ReceiveMessageFromAsync(buffer, SocketFlags.None, dummy.LocalEndPoint, cts.Token).AsTask()) - .TimeoutAfter(10_000); + .TimeoutAfter(30_000); Assert.Equal(cts.Token, ex.CancellationToken); } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index 16f85a3fa5288..0dba599af1b79 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -155,26 +155,32 @@ public async Task PreCanceled_Throws() [Fact] public async Task CancelDuringOperation_Throws() { - const int DatagramCount = 50; + const int DatagramCount = 100; const int DatagramSize = 32768; - TimeSpan cancelAfter = TimeSpan.FromMilliseconds(5); - using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - int port = client.BindToAnonymousPort(IPAddress.Any); + TimeSpan cancelAfter = TimeSpan.FromMilliseconds(1); - List tasks = new List(); - CancellationTokenSource cts = new CancellationTokenSource(); - - // After flooding the socket with a high number of send tasks, - // we assume some of them won't complete before the "CancelAfter" period expires. - for (int i = 0; i < DatagramCount; i++) + await RetryHelper.ExecuteAsync(async () => { - var leftTask = client.SendToAsync(new byte[DatagramSize], SocketFlags.None, ValidUdpRemoteEndpoint, cts.Token); - tasks.Add(leftTask.AsTask()); - } - cts.CancelAfter(cancelAfter); + cancelAfter /= 2; + + using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + int port = client.BindToAnonymousPort(IPAddress.Any); + + List tasks = new List(); + CancellationTokenSource cts = new CancellationTokenSource(); + + // After flooding the socket with a high number of send tasks, + // we assume some of them won't complete before the "CancelAfter" period expires. + for (int i = 0; i < DatagramCount; i++) + { + var leftTask = client.SendToAsync(new byte[DatagramSize], SocketFlags.None, ValidUdpRemoteEndpoint, cts.Token); + tasks.Add(leftTask.AsTask()); + } + cts.CancelAfter(cancelAfter); - await Assert.ThrowsAnyAsync(() => Task.WhenAll(tasks)); + await Assert.ThrowsAnyAsync(() => Task.WhenAll(tasks)); + }, maxAttempts: 4); } } From 3901fd18aa401230743166f3a0604e3bbf571bdd Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 21 Jan 2021 19:50:10 +0100 Subject: [PATCH 34/42] remove unnecessary $ --- .../src/System/Net/Sockets/Socket.Tasks.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs index b18daa269af0f..f5432d56e0009 100644 --- a/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs +++ b/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.Tasks.cs @@ -901,7 +901,7 @@ protected override void OnCompleted(SocketAsyncEventArgs _) /// This instance. public ValueTask ReceiveAsync(Socket socket, CancellationToken cancellationToken) { - Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); + Debug.Assert(Volatile.Read(ref _continuation) == null, "Expected null continuation to indicate reserved for use"); if (socket.ReceiveAsync(this, cancellationToken)) { @@ -921,7 +921,7 @@ public ValueTask ReceiveAsync(Socket socket, CancellationToken cancellation public ValueTask ReceiveFromAsync(Socket socket, CancellationToken cancellationToken) { - Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); + Debug.Assert(Volatile.Read(ref _continuation) == null, "Expected null continuation to indicate reserved for use"); if (socket.ReceiveFromAsync(this, cancellationToken)) { @@ -942,7 +942,7 @@ public ValueTask ReceiveFromAsync(Socket socket, Cancel public ValueTask ReceiveMessageFromAsync(Socket socket, CancellationToken cancellationToken) { - Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); + Debug.Assert(Volatile.Read(ref _continuation) == null, "Expected null continuation to indicate reserved for use"); if (socket.ReceiveMessageFromAsync(this, cancellationToken)) { @@ -967,7 +967,7 @@ public ValueTask ReceiveMessageFromAsync(Socket /// This instance. public ValueTask SendAsync(Socket socket, CancellationToken cancellationToken) { - Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); + Debug.Assert(Volatile.Read(ref _continuation) == null, "Expected null continuation to indicate reserved for use"); if (socket.SendAsync(this, cancellationToken)) { @@ -987,7 +987,7 @@ public ValueTask SendAsync(Socket socket, CancellationToken cancellationTok public ValueTask SendAsyncForNetworkStream(Socket socket, CancellationToken cancellationToken) { - Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); + Debug.Assert(Volatile.Read(ref _continuation) == null, "Expected null continuation to indicate reserved for use"); if (socket.SendAsync(this, cancellationToken)) { @@ -1006,7 +1006,7 @@ public ValueTask SendAsyncForNetworkStream(Socket socket, CancellationToken canc public ValueTask SendToAsync(Socket socket, CancellationToken cancellationToken) { - Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); + Debug.Assert(Volatile.Read(ref _continuation) == null, "Expected null continuation to indicate reserved for use"); if (socket.SendToAsync(this, cancellationToken)) { @@ -1026,7 +1026,7 @@ public ValueTask SendToAsync(Socket socket, CancellationToken cancellationT public ValueTask ConnectAsync(Socket socket) { - Debug.Assert(Volatile.Read(ref _continuation) == null, $"Expected null continuation to indicate reserved for use"); + Debug.Assert(Volatile.Read(ref _continuation) == null, "Expected null continuation to indicate reserved for use"); try { From b2b893df3fe6804cb8690445a23ebc0b9fda9047 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 22 Jan 2021 15:45:50 +0100 Subject: [PATCH 35/42] Workaround for #47335 --- .../tests/FunctionalTests/ReceiveMessageFrom.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index 1941657a5372f..c0bc43eb8025d 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -27,6 +27,10 @@ public async Task ReceiveSent_TCP_Success(bool ipv6) using (sender) using (receiver) { + // accept() will create a DualMode socket on Mac (https://github.com/dotnet/runtime/issues/47335), + // while recvmsg() does not work with DualMode on that OS, so we need to flip the flag back: + if (ipv6) receiver.DualMode = false; + byte[] sendBuffer = { 1, 2, 3 }; sender.Send(sendBuffer); From 5b00ee6cfbfeb7f9ed11631b56a65a00b067a091 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 22 Jan 2021 15:55:59 +0100 Subject: [PATCH 36/42] better timeout handling --- .../tests/FunctionalTests/ReceiveFrom.cs | 12 ++++++++---- .../tests/FunctionalTests/ReceiveMessageFrom.cs | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 98a3b6b4ca665..72b67a8725fe5 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -14,6 +14,8 @@ namespace System.Net.Sockets.Tests protected static IPEndPoint GetGetDummyTestEndpoint(AddressFamily addressFamily = AddressFamily.InterNetwork) => addressFamily == AddressFamily.InterNetwork ? new IPEndPoint(IPAddress.Parse("1.2.3.4"), 1234) : new IPEndPoint(IPAddress.Parse("1:2:3::4"), 1234); + protected static readonly TimeSpan CancellationTestTimeout = TimeSpan.FromSeconds(30); + protected ReceiveFrom(ITestOutputHelper output) : base(output) { } [Theory] @@ -99,11 +101,13 @@ public async Task ClosedDuringOperation_Throws_ObjectDisposedExceptionOrSocketEx if (UsesApm) { - await Assert.ThrowsAsync(() => receiveTask); + await Assert.ThrowsAsync(() => receiveTask) + .TimeoutAfter(CancellationTestTimeout); } else { - SocketException ex = await Assert.ThrowsAsync(() => receiveTask); + SocketException ex = await Assert.ThrowsAsync(() => receiveTask) + .TimeoutAfter(CancellationTestTimeout); SocketError expectedError = UsesSync ? SocketError.Interrupted : SocketError.OperationAborted; Assert.Equal(expectedError, ex.SocketErrorCode); } @@ -120,7 +124,7 @@ public async Task ShutdownReceiveBeforeOperation_ThrowsSocketException(SocketShu socket.Shutdown(shutdown); SocketException exception = await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())) - .TimeoutAfter(10_000); + .TimeoutAfter(CancellationTestTimeout); Assert.Equal(SocketError.Shutdown, exception.SocketErrorCode); } @@ -182,7 +186,7 @@ public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) OperationCanceledException ex = await Assert.ThrowsAnyAsync( () => socket.ReceiveFromAsync(buffer, SocketFlags.None, dummy.LocalEndPoint, cts.Token).AsTask()) - .TimeoutAfter(30_000); + .TimeoutAfter(CancellationTestTimeout); Assert.Equal(cts.Token, ex.CancellationToken); } } diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index c0bc43eb8025d..d679897404473 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -15,6 +15,8 @@ namespace System.Net.Sockets.Tests protected static IPEndPoint GetGetDummyTestEndpoint(AddressFamily addressFamily = AddressFamily.InterNetwork) => addressFamily == AddressFamily.InterNetwork ? new IPEndPoint(IPAddress.Parse("1.2.3.4"), 1234) : new IPEndPoint(IPAddress.Parse("1:2:3::4"), 1234); + protected static readonly TimeSpan CancellationTestTimeout = TimeSpan.FromSeconds(30); + protected ReceiveMessageFrom(ITestOutputHelper output) : base(output) { } [PlatformSpecific(TestPlatforms.AnyUnix)] @@ -109,11 +111,13 @@ public async Task ClosedDuringOperation_Throws_ObjectDisposedExceptionOrSocketEx if (UsesApm) { - await Assert.ThrowsAsync(() => receiveTask); + await Assert.ThrowsAsync(() => receiveTask) + .TimeoutAfter(CancellationTestTimeout); } else { - SocketException ex = await Assert.ThrowsAsync(() => receiveTask); + SocketException ex = await Assert.ThrowsAsync(() => receiveTask) + .TimeoutAfter(CancellationTestTimeout); SocketError expectedError = UsesSync ? SocketError.Interrupted : SocketError.OperationAborted; Assert.Equal(expectedError, ex.SocketErrorCode); } @@ -130,7 +134,7 @@ public async Task ShutdownReceiveBeforeOperation_ThrowsSocketException(SocketShu socket.Shutdown(shutdown); SocketException exception = await Assert.ThrowsAnyAsync(() => ReceiveMessageFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())) - .TimeoutAfter(10_000); + .TimeoutAfter(CancellationTestTimeout); Assert.Equal(SocketError.Shutdown, exception.SocketErrorCode); } @@ -192,7 +196,7 @@ public async Task WhenCanceled_Throws(IPAddress loopback, bool precanceled) OperationCanceledException ex = await Assert.ThrowsAnyAsync( () => socket.ReceiveMessageFromAsync(buffer, SocketFlags.None, dummy.LocalEndPoint, cts.Token).AsTask()) - .TimeoutAfter(30_000); + .TimeoutAfter(CancellationTestTimeout); Assert.Equal(cts.Token, ex.CancellationToken); } } From a34533259271321a2eacab9707671d77c01b670f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 22 Jan 2021 18:07:34 +0100 Subject: [PATCH 37/42] ignore synchronous Dispose tests on Mac (#47342) --- .../tests/FunctionalTests/ReceiveFrom.cs | 7 +++++++ .../tests/FunctionalTests/ReceiveMessageFrom.cs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 72b67a8725fe5..8017f2490f9ab 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -91,6 +91,13 @@ public async Task ClosedBeforeOperation_Throws_ObjectDisposedException(bool clos [InlineData(false)] public async Task ClosedDuringOperation_Throws_ObjectDisposedExceptionOrSocketException(bool closeOrDispose) { + if (UsesSync && PlatformDetection.IsOSX) + { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/47342")] + // On Mac, Close/Dispose hangs when invoked concurrently with a blocking UDP receive. + return; + } + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.BindToAnonymousPort(IPAddress.Any); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index d679897404473..0be5f488def0d 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -101,6 +101,13 @@ public async Task ClosedBeforeOperation_Throws_ObjectDisposedException(bool clos [InlineData(false)] public async Task ClosedDuringOperation_Throws_ObjectDisposedExceptionOrSocketException(bool closeOrDispose) { + if (UsesSync && PlatformDetection.IsOSX) + { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/47342")] + // On Mac, Close/Dispose hangs when invoked concurrently with a blocking UDP receive. + return; + } + using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.BindToAnonymousPort(IPAddress.Any); From 52ebf91f4667e53e97546716c3ba2b7a755a92cf Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 22 Jan 2021 18:36:03 +0100 Subject: [PATCH 38/42] update workaround in ReceiveSent_TCP_Success --- .../tests/FunctionalTests/ReceiveMessageFrom.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index 0be5f488def0d..1c467a9b128e8 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -25,14 +25,20 @@ protected ReceiveMessageFrom(ITestOutputHelper output) : base(output) { } [InlineData(true)] public async Task ReceiveSent_TCP_Success(bool ipv6) { + if (ipv6 && PlatformDetection.IsOSX) + { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/47335")] + // accept() will create a (seemingly) DualMode socket on Mac, + // but since recvmsg() does not work with DualMode on that OS, we throw PNSE CheckDualModeReceiveSupport(). + // Weirdly, the flag is readable, but an attempt to write it leads to EINVAL. + // The best option seems to be to skip this test for the Mac+IPV6 case + return; + } + (Socket sender, Socket receiver) = SocketTestExtensions.CreateConnectedSocketPair(ipv6); using (sender) using (receiver) { - // accept() will create a DualMode socket on Mac (https://github.com/dotnet/runtime/issues/47335), - // while recvmsg() does not work with DualMode on that OS, so we need to flip the flag back: - if (ipv6) receiver.DualMode = false; - byte[] sendBuffer = { 1, 2, 3 }; sender.Send(sendBuffer); From 6237c314cd30a0136514840af1f048c4d2bc6568 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 25 Jan 2021 15:22:14 +0100 Subject: [PATCH 39/42] fix ReceiveFrom.ReceiveSent_TCP_Success, finetune SebdTo.CancelDuringOperation_Throws --- .../tests/FunctionalTests/ReceiveFrom.cs | 10 ++++++++++ .../System.Net.Sockets/tests/FunctionalTests/SendTo.cs | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 8017f2490f9ab..99c0380898cb3 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -59,6 +59,16 @@ public async Task NullEndpoint_Throws() [InlineData(true)] public async Task ReceiveSent_TCP_Success(bool ipv6) { + if (ipv6 && PlatformDetection.IsOSX) + { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/47335")] + // accept() will create a (seemingly) DualMode socket on Mac, + // but since recvmsg() does not work with DualMode on that OS, we throw PNSE CheckDualModeReceiveSupport(). + // Weirdly, the flag is readable, but an attempt to write it leads to EINVAL. + // The best option seems to be to skip this test for the Mac+IPV6 case + return; + } + (Socket sender, Socket receiver) = SocketTestExtensions.CreateConnectedSocketPair(ipv6); using (sender) using (receiver) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index 0dba599af1b79..5602d4a0c746c 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -156,9 +156,9 @@ public async Task PreCanceled_Throws() public async Task CancelDuringOperation_Throws() { const int DatagramCount = 100; - const int DatagramSize = 32768; + const int DatagramSize = 9216; // Default maximum datagram size on Mac - TimeSpan cancelAfter = TimeSpan.FromMilliseconds(1); + TimeSpan cancelAfter = TimeSpan.FromMilliseconds(2); await RetryHelper.ExecuteAsync(async () => { @@ -180,7 +180,7 @@ await RetryHelper.ExecuteAsync(async () => cts.CancelAfter(cancelAfter); await Assert.ThrowsAnyAsync(() => Task.WhenAll(tasks)); - }, maxAttempts: 4); + }, maxAttempts: 7); } } From 6f43cb2f17bdb6cd23fee8bf0e38fa3f85989575 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 25 Jan 2021 20:21:34 +0100 Subject: [PATCH 40/42] delete CancelDuringOperation_Throws --- .../tests/FunctionalTests/SendTo.cs | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index 5602d4a0c746c..c1e3da5fb5292 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -146,42 +146,6 @@ public async Task PreCanceled_Throws() Assert.Equal(cts.Token, ex.CancellationToken); } - - // On Unix, flooding the kernel with sendto calls will lead to actual asynchronous completions, - // so we can test the cancellation logic implemented in SocketAsyncContext. - // On Windows, WSASendTo/WSASendMsg implementations are not cancellable with CancelIoEx as of 01/2021, - // which means CancellationToken will only take effect if it's precancelled. This may change in the future. - [PlatformSpecific(TestPlatforms.AnyUnix)] - [Fact] - public async Task CancelDuringOperation_Throws() - { - const int DatagramCount = 100; - const int DatagramSize = 9216; // Default maximum datagram size on Mac - - TimeSpan cancelAfter = TimeSpan.FromMilliseconds(2); - - await RetryHelper.ExecuteAsync(async () => - { - cancelAfter /= 2; - - using Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - int port = client.BindToAnonymousPort(IPAddress.Any); - - List tasks = new List(); - CancellationTokenSource cts = new CancellationTokenSource(); - - // After flooding the socket with a high number of send tasks, - // we assume some of them won't complete before the "CancelAfter" period expires. - for (int i = 0; i < DatagramCount; i++) - { - var leftTask = client.SendToAsync(new byte[DatagramSize], SocketFlags.None, ValidUdpRemoteEndpoint, cts.Token); - tasks.Add(leftTask.AsTask()); - } - cts.CancelAfter(cancelAfter); - - await Assert.ThrowsAnyAsync(() => Task.WhenAll(tasks)); - }, maxAttempts: 7); - } } public sealed class SendTo_MemoryArrayTask : SendToBase From c1bd86a8f11a27fc5e7b52e7bffeb5dc64ef15bb Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 26 Jan 2021 17:28:10 +0100 Subject: [PATCH 41/42] Trying to workaround #47469 with a delay --- .../System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs | 4 ++++ .../tests/FunctionalTests/ReceiveMessageFrom.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs index 99c0380898cb3..939595d5f4beb 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveFrom.cs @@ -140,6 +140,10 @@ public async Task ShutdownReceiveBeforeOperation_ThrowsSocketException(SocketShu socket.BindToAnonymousPort(IPAddress.Any); socket.Shutdown(shutdown); + // [ActiveIssue("https://github.com/dotnet/runtime/issues/47469")] + // Shutdown(Both) does not seem to take immediate effect for Receive(Message)From in a consistent manner, trying to workaround with a delay: + if (shutdown == SocketShutdown.Both) await Task.Delay(50); + SocketException exception = await Assert.ThrowsAnyAsync(() => ReceiveFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())) .TimeoutAfter(CancellationTestTimeout); diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs index 1c467a9b128e8..e23c84fb0b1e4 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFrom.cs @@ -146,6 +146,10 @@ public async Task ShutdownReceiveBeforeOperation_ThrowsSocketException(SocketShu socket.BindToAnonymousPort(IPAddress.Any); socket.Shutdown(shutdown); + // [ActiveIssue("https://github.com/dotnet/runtime/issues/47469")] + // Shutdown(Both) does not seem to take immediate effect for Receive(Message)From in a consistent manner, trying to workaround with a delay: + if (shutdown == SocketShutdown.Both) await Task.Delay(50); + SocketException exception = await Assert.ThrowsAnyAsync(() => ReceiveMessageFromAsync(socket, new byte[1], GetGetDummyTestEndpoint())) .TimeoutAfter(CancellationTestTimeout); From b2f6634a13e3b5dc3b22bfc27341068bb77b87d6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 27 Jan 2021 11:50:17 +0100 Subject: [PATCH 42/42] SendToBase -> SendTo --- .../tests/FunctionalTests/SendTo.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs index c1e3da5fb5292..1159d40f2e4a3 100644 --- a/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs +++ b/src/libraries/System.Net.Sockets/tests/FunctionalTests/SendTo.cs @@ -11,11 +11,11 @@ namespace System.Net.Sockets.Tests { - public abstract class SendToBase : SocketTestHelperBase where T : SocketHelperBase, new() + public abstract class SendTo : SocketTestHelperBase where T : SocketHelperBase, new() { protected static readonly IPEndPoint ValidUdpRemoteEndpoint = new IPEndPoint(IPAddress.Parse("10.20.30.40"), 1234); - protected SendToBase(ITestOutputHelper output) : base(output) + protected SendTo(ITestOutputHelper output) : base(output) { } @@ -95,42 +95,42 @@ public async Task Disposed_Throws() } } - public sealed class SendTo_SyncSpan : SendToBase + public sealed class SendTo_SyncSpan : SendTo { public SendTo_SyncSpan(ITestOutputHelper output) : base(output) { } } - public sealed class SendTo_SyncSpanForceNonBlocking : SendToBase + public sealed class SendTo_SyncSpanForceNonBlocking : SendTo { public SendTo_SyncSpanForceNonBlocking(ITestOutputHelper output) : base(output) { } } - public sealed class SendTo_ArraySync : SendToBase + public sealed class SendTo_ArraySync : SendTo { public SendTo_ArraySync(ITestOutputHelper output) : base(output) { } } - public sealed class SendTo_SyncForceNonBlocking : SendToBase + public sealed class SendTo_SyncForceNonBlocking : SendTo { public SendTo_SyncForceNonBlocking(ITestOutputHelper output) : base(output) {} } - public sealed class SendTo_Apm : SendToBase + public sealed class SendTo_Apm : SendTo { public SendTo_Apm(ITestOutputHelper output) : base(output) {} } - public sealed class SendTo_Eap : SendToBase + public sealed class SendTo_Eap : SendTo { public SendTo_Eap(ITestOutputHelper output) : base(output) {} } - public sealed class SendTo_Task : SendToBase + public sealed class SendTo_Task : SendTo { public SendTo_Task(ITestOutputHelper output) : base(output) { } } - public sealed class SendTo_CancellableTask : SendToBase + public sealed class SendTo_CancellableTask : SendTo { public SendTo_CancellableTask(ITestOutputHelper output) : base(output) { } @@ -148,12 +148,12 @@ public async Task PreCanceled_Throws() } } - public sealed class SendTo_MemoryArrayTask : SendToBase + public sealed class SendTo_MemoryArrayTask : SendTo { public SendTo_MemoryArrayTask(ITestOutputHelper output) : base(output) { } } - public sealed class SendTo_MemoryNativeTask : SendToBase + public sealed class SendTo_MemoryNativeTask : SendTo { public SendTo_MemoryNativeTask(ITestOutputHelper output) : base(output) { } }