WaitForConnectionAsyncCore();
async Task WaitForConnectionAsyncCore() =>
- HandleAcceptedSocket(await _instance!.ListeningSocket.AcceptAsync().ConfigureAwait(false));
+ HandleAcceptedSocket(await _instance!.ListeningSocket.AcceptAsync(cancellationToken).ConfigureAwait(false));
}
private void HandleAcceptedSocket(Socket acceptedSocket)
var ctx = new CancellationTokenSource();
- if (OperatingSystem.IsWindows()) // cancellation token after the operation has been initiated
- {
- Task serverWaitTimeout = server.WaitForConnectionAsync(ctx.Token);
- ctx.Cancel();
- await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverWaitTimeout);
- }
-
+ Task serverWaitTimeout = server.WaitForConnectionAsync(ctx.Token);
ctx.Cancel();
+ await Assert.ThrowsAnyAsync<OperationCanceledException>(() => serverWaitTimeout);
+
Assert.True(server.WaitForConnectionAsync(ctx.Token).IsCanceled);
}
public bool UseOnlyOverlappedIO { get { throw null; } set { } }
public System.Net.Sockets.Socket Accept() { throw null; }
public System.Threading.Tasks.Task<System.Net.Sockets.Socket> AcceptAsync() { throw null; }
+ public System.Threading.Tasks.ValueTask<System.Net.Sockets.Socket> AcceptAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
public System.Threading.Tasks.Task<System.Net.Sockets.Socket> AcceptAsync(System.Net.Sockets.Socket? acceptSocket) { throw null; }
+ public System.Threading.Tasks.ValueTask<System.Net.Sockets.Socket> AcceptAsync(System.Net.Sockets.Socket? acceptSocket, System.Threading.CancellationToken cancellationToken) { throw null; }
public bool AcceptAsync(System.Net.Sockets.SocketAsyncEventArgs e) { throw null; }
public System.IAsyncResult BeginAccept(System.AsyncCallback? callback, object? state) { throw null; }
public System.IAsyncResult BeginAccept(int receiveSize, System.AsyncCallback? callback, object? state) { throw null; }
public System.Net.Sockets.Socket Server { get { throw null; } }
public System.Net.Sockets.Socket AcceptSocket() { throw null; }
public System.Threading.Tasks.Task<System.Net.Sockets.Socket> AcceptSocketAsync() { throw null; }
+ public System.Threading.Tasks.ValueTask<System.Net.Sockets.Socket> AcceptSocketAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
public System.Net.Sockets.TcpClient AcceptTcpClient() { throw null; }
public System.Threading.Tasks.Task<System.Net.Sockets.TcpClient> AcceptTcpClientAsync() { throw null; }
+ public System.Threading.Tasks.ValueTask<System.Net.Sockets.TcpClient> AcceptTcpClientAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
[System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")]
public void AllowNatTraversal(bool allowed) { }
public System.IAsyncResult BeginAcceptSocket(System.AsyncCallback? callback, object? state) { throw null; }
{
public partial class Socket
{
- /// <summary>Cached instance for accept operations.</summary>
- private TaskSocketAsyncEventArgs<Socket>? _acceptEventArgs;
-
/// <summary>Cached instance for receive operations that return <see cref="ValueTask{Int32}"/>. Also used for ConnectAsync operations.</summary>
private AwaitableSocketAsyncEventArgs? _singleBufferReceiveEventArgs;
- /// <summary>Cached instance for send operations that return <see cref="ValueTask{Int32}"/>.</summary>
+ /// <summary>Cached instance for send operations that return <see cref="ValueTask{Int32}"/>. Also used for AcceptAsync operations.</summary>
private AwaitableSocketAsyncEventArgs? _singleBufferSendEventArgs;
/// <summary>Cached instance for receive operations that return <see cref="Task{Int32}"/>.</summary>
/// Accepts an incoming connection.
/// </summary>
/// <returns>An asynchronous task that completes with the accepted Socket.</returns>
- public Task<Socket> AcceptAsync() => AcceptAsync((Socket?)null);
+ public Task<Socket> AcceptAsync() => AcceptAsync((Socket?)null, CancellationToken.None).AsTask();
/// <summary>
/// Accepts an incoming connection.
/// </summary>
- /// <param name="acceptSocket">The socket to use for accepting the connection.</param>
+ /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>An asynchronous task that completes with the accepted Socket.</returns>
- public Task<Socket> AcceptAsync(Socket? acceptSocket)
- {
- // Get any cached SocketAsyncEventArg we may have.
- TaskSocketAsyncEventArgs<Socket>? saea = Interlocked.Exchange(ref _acceptEventArgs, null);
- if (saea is null)
- {
- saea = new TaskSocketAsyncEventArgs<Socket>();
- saea.Completed += (s, e) => CompleteAccept((Socket)s!, (TaskSocketAsyncEventArgs<Socket>)e);
- }
+ public ValueTask<Socket> AcceptAsync(CancellationToken cancellationToken) => AcceptAsync((Socket?)null, cancellationToken);
- // Configure the SAEA.
- saea.AcceptSocket = acceptSocket;
+ /// <summary>
+ /// Accepts an incoming connection.
+ /// </summary>
+ /// <param name="acceptSocket">The socket to use for accepting the connection.</param>
+ /// <returns>An asynchronous task that completes with the accepted Socket.</returns>
+ public Task<Socket> AcceptAsync(Socket? acceptSocket) => AcceptAsync(acceptSocket, CancellationToken.None).AsTask();
- // Initiate the accept operation.
- Task<Socket> t;
- if (AcceptAsync(saea))
+ /// <summary>
+ /// Accepts an incoming connection.
+ /// </summary>
+ /// <param name="acceptSocket">The socket to use for accepting the connection.</param>
+ /// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
+ /// <returns>An asynchronous task that completes with the accepted Socket.</returns>
+ public ValueTask<Socket> AcceptAsync(Socket? acceptSocket, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
{
- // The operation is completing asynchronously (it may have already completed).
- // Get the task for the operation, with appropriate synchronization to coordinate
- // with the async callback that'll be completing the task.
- bool responsibleForReturningToPool;
- t = saea.GetCompletionResponsibility(out responsibleForReturningToPool).Task;
- if (responsibleForReturningToPool)
- {
- // We're responsible for returning it only if the callback has already been invoked
- // and gotten what it needs from the SAEA; otherwise, the callback will return it.
- ReturnSocketAsyncEventArgs(saea);
- }
+ return ValueTask.FromCanceled<Socket>(cancellationToken);
}
- else
- {
- // The operation completed synchronously. Get a task for it.
- t = saea.SocketError == SocketError.Success ?
- Task.FromResult(saea.AcceptSocket!) :
- Task.FromException<Socket>(GetException(saea.SocketError));
- // There won't be a callback, and we're done with the SAEA, so return it to the pool.
- ReturnSocketAsyncEventArgs(saea);
- }
+ AwaitableSocketAsyncEventArgs saea =
+ Interlocked.Exchange(ref _singleBufferSendEventArgs, null) ??
+ new AwaitableSocketAsyncEventArgs(this, isReceiveForCaching: false);
- return t;
+ Debug.Assert(saea.BufferList == null);
+ saea.SetBuffer(null, 0, 0);
+ saea.AcceptSocket = acceptSocket;
+ saea.WrapExceptionsForNetworkStream = false;
+ return saea.AcceptAsync(this, cancellationToken);
}
/// <summary>
return t;
}
- /// <summary>Completes the SocketAsyncEventArg's Task with the result of the send or receive, and returns it to the specified pool.</summary>
- private static void CompleteAccept(Socket s, TaskSocketAsyncEventArgs<Socket> saea)
- {
- // Pull the relevant state off of the SAEA
- SocketError error = saea.SocketError;
- Socket? acceptSocket = saea.AcceptSocket;
-
- // Synchronize with the initiating thread. If the synchronous caller already got what
- // it needs from the SAEA, then we can return it to the pool now. Otherwise, it'll be
- // responsible for returning it once it's gotten what it needs from it.
- bool responsibleForReturningToPool;
- AsyncTaskMethodBuilder<Socket> builder = saea.GetCompletionResponsibility(out responsibleForReturningToPool);
- if (responsibleForReturningToPool)
- {
- s.ReturnSocketAsyncEventArgs(saea);
- }
-
- // Complete the builder/task with the results.
- if (error == SocketError.Success)
- {
- builder.SetResult(acceptSocket!);
- }
- else
- {
- builder.SetException(GetException(error));
- }
- }
-
/// <summary>Completes the SocketAsyncEventArg's Task with the result of the send or receive, and returns it to the specified pool.</summary>
private static void CompleteSendReceive(Socket s, TaskSocketAsyncEventArgs<int> saea, bool isReceive)
{
}
}
- /// <summary>Returns a <see cref="TaskSocketAsyncEventArgs{TResult}"/> instance for reuse.</summary>
- /// <param name="saea">The instance to return.</param>
- private void ReturnSocketAsyncEventArgs(TaskSocketAsyncEventArgs<Socket> saea)
- {
- // Reset state on the SAEA before returning it. But do not reset buffer state. That'll be done
- // if necessary by the consumer, but we want to keep the buffers due to likely subsequent reuse
- // and the costs associated with changing them.
- saea.AcceptSocket = null;
- saea._accessed = false;
- saea._builder = default;
-
- // Write this instance back as a cached instance, only if there isn't currently one cached.
- if (Interlocked.CompareExchange(ref _acceptEventArgs, saea, null) != null)
- {
- // Couldn't return it, so dispose it.
- saea.Dispose();
- }
- }
-
/// <summary>Dispose of any cached <see cref="TaskSocketAsyncEventArgs{TResult}"/> instances.</summary>
private void DisposeCachedTaskSocketAsyncEventArgs()
{
- Interlocked.Exchange(ref _acceptEventArgs, null)?.Dispose();
Interlocked.Exchange(ref _multiBufferReceiveEventArgs, null)?.Dispose();
Interlocked.Exchange(ref _multiBufferSendEventArgs, null)?.Dispose();
Interlocked.Exchange(ref _singleBufferReceiveEventArgs, null)?.Dispose();
}
/// <summary>A SocketAsyncEventArgs that can be awaited to get the result of an operation.</summary>
- internal sealed class AwaitableSocketAsyncEventArgs : SocketAsyncEventArgs, IValueTaskSource, IValueTaskSource<int>, IValueTaskSource<SocketReceiveFromResult>, IValueTaskSource<SocketReceiveMessageFromResult>
+ internal sealed class AwaitableSocketAsyncEventArgs : SocketAsyncEventArgs, IValueTaskSource, IValueTaskSource<int>, IValueTaskSource<Socket>, IValueTaskSource<SocketReceiveFromResult>, IValueTaskSource<SocketReceiveMessageFromResult>
{
private static readonly Action<object?> s_completedSentinel = new Action<object?>(state => throw new InvalidOperationException(SR.Format(SR.net_sockets_valuetaskmisuse, nameof(s_completedSentinel))));
/// <summary>The owning socket.</summary>
}
}
+ /// <summary>Initiates an accept operation on the associated socket.</summary>
+ /// <returns>This instance.</returns>
+ public ValueTask<Socket> AcceptAsync(Socket socket, CancellationToken cancellationToken)
+ {
+ Debug.Assert(Volatile.Read(ref _continuation) == null, "Expected null continuation to indicate reserved for use");
+
+ if (socket.AcceptAsync(this, cancellationToken))
+ {
+ _cancellationToken = cancellationToken;
+ return new ValueTask<Socket>(this, _token);
+ }
+
+ Socket acceptSocket = AcceptSocket!;
+ SocketError error = SocketError;
+
+ Release();
+
+ return error == SocketError.Success ?
+ new ValueTask<Socket>(acceptSocket) :
+ ValueTask.FromException<Socket>(CreateException(error));
+ }
+
/// <summary>Initiates a receive operation on the associated socket.</summary>
/// <returns>This instance.</returns>
public ValueTask<int> ReceiveAsync(Socket socket, CancellationToken cancellationToken)
/// Unlike TaskAwaiter's GetResult, this does not block until the operation completes: it must only
/// be used once the operation has completed. This is handled implicitly by await.
/// </remarks>
- public int GetResult(short token)
+ int IValueTaskSource<int>.GetResult(short token)
{
if (token != _token)
{
}
}
+ Socket IValueTaskSource<Socket>.GetResult(short token)
+ {
+ if (token != _token)
+ {
+ ThrowIncorrectTokenException();
+ }
+
+ SocketError error = SocketError;
+ Socket acceptSocket = AcceptSocket!;
+ CancellationToken cancellationToken = _cancellationToken;
+
+ Release();
+
+ if (error != SocketError.Success)
+ {
+ ThrowException(error, cancellationToken);
+ }
+ return acceptSocket;
+ }
+
SocketReceiveFromResult IValueTaskSource<SocketReceiveFromResult>.GetResult(short token)
{
if (token != _token)
// Async methods
//
- public bool AcceptAsync(SocketAsyncEventArgs e)
+ public bool AcceptAsync(SocketAsyncEventArgs e) => AcceptAsync(e, CancellationToken.None);
+
+ private bool AcceptAsync(SocketAsyncEventArgs e, CancellationToken cancellationToken)
{
ThrowIfDisposed();
SocketError socketError;
try
{
- socketError = e.DoOperationAccept(this, _handle, acceptHandle);
+ socketError = e.DoOperationAccept(this, _handle, acceptHandle, cancellationToken);
}
catch (Exception ex)
{
return operation.ErrorCode;
}
- public SocketError AcceptAsync(byte[] socketAddress, ref int socketAddressLen, out IntPtr acceptedFd, Action<IntPtr, byte[], int, SocketError> callback)
+ public SocketError AcceptAsync(byte[] socketAddress, ref int socketAddressLen, out IntPtr acceptedFd, Action<IntPtr, byte[], int, SocketError> callback, CancellationToken cancellationToken)
{
Debug.Assert(socketAddress != null, "Expected non-null socketAddress");
Debug.Assert(socketAddressLen > 0, $"Unexpected socketAddressLen: {socketAddressLen}");
operation.SocketAddress = socketAddress;
operation.SocketAddressLen = socketAddressLen;
- if (!_receiveQueue.StartAsyncOperation(this, operation, observedSequenceNumber))
+ if (!_receiveQueue.StartAsyncOperation(this, operation, observedSequenceNumber, cancellationToken))
{
socketAddressLen = operation.SocketAddressLen;
acceptedFd = operation.AcceptedFileDescriptor;
_acceptAddressBufferCount = socketAddressSize;
}
- internal unsafe SocketError DoOperationAccept(Socket socket, SafeSocketHandle handle, SafeSocketHandle? acceptHandle)
+ internal unsafe SocketError DoOperationAccept(Socket socket, SafeSocketHandle handle, SafeSocketHandle? acceptHandle, CancellationToken cancellationToken)
{
if (!_buffer.Equals(default))
{
IntPtr acceptedFd;
int socketAddressLen = _acceptAddressBufferCount / 2;
- SocketError socketError = handle.AsyncContext.AcceptAsync(_acceptBuffer!, ref socketAddressLen, out acceptedFd, AcceptCompletionCallback);
+ SocketError socketError = handle.AsyncContext.AcceptAsync(_acceptBuffer!, ref socketAddressLen, out acceptedFd, AcceptCompletionCallback, cancellationToken);
if (socketError != SocketError.IOPending)
{
return socketError;
}
- internal unsafe SocketError DoOperationAccept(Socket socket, SafeSocketHandle handle, SafeSocketHandle acceptHandle)
+ internal unsafe SocketError DoOperationAccept(Socket socket, SafeSocketHandle handle, SafeSocketHandle acceptHandle, CancellationToken cancellationToken)
{
bool userBuffer = _count != 0;
Debug.Assert(!userBuffer || (!_buffer.Equals(default) && _count >= _acceptAddressBufferCount));
Memory<byte> buffer = userBuffer ? _buffer : _acceptBuffer;
- Debug.Assert(_asyncProcessingState == AsyncProcessingState.None);
- NativeOverlapped* overlapped = AllocateNativeOverlapped();
- try
+ fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer.Span))
{
- _singleBufferHandle = buffer.Pin();
- _asyncProcessingState = AsyncProcessingState.Set;
+ NativeOverlapped* overlapped = AllocateNativeOverlapped();
+ try
+ {
+ Debug.Assert(_asyncProcessingState == AsyncProcessingState.None, $"Expected None, got {_asyncProcessingState}");
+ _asyncProcessingState = AsyncProcessingState.InProcess;
- bool success = socket.AcceptEx(
- handle,
- acceptHandle,
- userBuffer ? (IntPtr)((byte*)_singleBufferHandle.Pointer + _offset) : (IntPtr)_singleBufferHandle.Pointer,
- userBuffer ? _count - _acceptAddressBufferCount : 0,
- _acceptAddressBufferCount / 2,
- _acceptAddressBufferCount / 2,
- out int bytesTransferred,
- overlapped);
+ bool success = socket.AcceptEx(
+ handle,
+ acceptHandle,
+ (IntPtr)(userBuffer ? (bufferPtr + _offset) : bufferPtr),
+ userBuffer ? _count - _acceptAddressBufferCount : 0,
+ _acceptAddressBufferCount / 2,
+ _acceptAddressBufferCount / 2,
+ out int bytesTransferred,
+ overlapped);
- return ProcessIOCPResult(success, bytesTransferred, overlapped);
- }
- catch
- {
- _asyncProcessingState = AsyncProcessingState.None;
- FreeNativeOverlapped(overlapped);
- _singleBufferHandle.Dispose();
- throw;
+ return ProcessIOCPResultWithDeferredAsyncHandling(success, bytesTransferred, overlapped, buffer, cancellationToken);
+ }
+ catch
+ {
+ _asyncProcessingState = AsyncProcessingState.None;
+ FreeNativeOverlapped(overlapped);
+ throw;
+ }
}
}
safeHandle.DangerousAddRef(ref refAdded);
IntPtr handle = safeHandle.DangerousGetHandle();
- Debug.Assert(_asyncProcessingState == AsyncProcessingState.Set);
- bool userBuffer = _count >= _acceptAddressBufferCount;
-
- _currentSocket.GetAcceptExSockaddrs(
- userBuffer ? (IntPtr)((byte*)_singleBufferHandle.Pointer + _offset) : (IntPtr)_singleBufferHandle.Pointer,
- _count != 0 ? _count - _acceptAddressBufferCount : 0,
- _acceptAddressBufferCount / 2,
- _acceptAddressBufferCount / 2,
- out localAddr,
- out localAddrLength,
- out remoteAddr,
- out remoteSocketAddress.InternalSize
+ // This matches the logic in DoOperationAccept
+ bool userBuffer = _count != 0;
+ Debug.Assert(!userBuffer || (!_buffer.Equals(default) && _count >= _acceptAddressBufferCount));
+ Memory<byte> buffer = userBuffer ? _buffer : _acceptBuffer;
+
+ fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer.Span))
+ {
+ _currentSocket.GetAcceptExSockaddrs(
+ (IntPtr)(userBuffer ? (bufferPtr + _offset) : bufferPtr),
+ userBuffer ? _count - _acceptAddressBufferCount : 0,
+ _acceptAddressBufferCount / 2,
+ _acceptAddressBufferCount / 2,
+ out localAddr,
+ out localAddrLength,
+ out remoteAddr,
+ out remoteSocketAddress.InternalSize
);
- Marshal.Copy(remoteAddr, remoteSocketAddress.Buffer, 0, remoteSocketAddress.Size);
+
+ Marshal.Copy(remoteAddr, remoteSocketAddress.Buffer, 0, remoteSocketAddress.Size);
+ }
socketError = Interop.Winsock.setsockopt(
_acceptSocket!.SafeHandle,
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Threading;
using System.Threading.Tasks;
using System.Runtime.Versioning;
using System.Diagnostics;
public TcpClient EndAcceptTcpClient(IAsyncResult asyncResult) =>
EndAcceptCore<TcpClient>(asyncResult);
- public Task<Socket> AcceptSocketAsync()
+ public Task<Socket> AcceptSocketAsync() => AcceptSocketAsync(CancellationToken.None).AsTask();
+
+ public ValueTask<Socket> AcceptSocketAsync(CancellationToken cancellationToken)
{
if (!_active)
{
throw new InvalidOperationException(SR.net_stopped);
}
- return _serverSocket!.AcceptAsync();
+ return _serverSocket!.AcceptAsync(cancellationToken);
}
- public Task<TcpClient> AcceptTcpClientAsync()
+ public Task<TcpClient> AcceptTcpClientAsync() => AcceptTcpClientAsync(CancellationToken.None).AsTask();
+
+ public ValueTask<TcpClient> AcceptTcpClientAsync(CancellationToken cancellationToken)
{
- return WaitAndWrap(AcceptSocketAsync());
+ return WaitAndWrap(AcceptSocketAsync(cancellationToken));
- static async Task<TcpClient> WaitAndWrap(Task<Socket> task) =>
+ static async ValueTask<TcpClient> WaitAndWrap(ValueTask<Socket> task) =>
new TcpClient(await task.ConfigureAwait(false));
}
-
// This creates a TcpListener that listens on both IPv4 and IPv6 on the given port.
public static TcpListener Create(int port)
{
public AcceptTask(ITestOutputHelper output) : base(output) {}
}
+ public sealed class AcceptCancellableTask : Accept<SocketHelperCancellableTask>
+ {
+ public AcceptCancellableTask(ITestOutputHelper output) : base(output) { }
+
+ [Fact]
+ public async Task AcceptAsync_Precanceled_Throws()
+ {
+ using (Socket listen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
+ {
+ int port = listen.BindToAnonymousPort(IPAddress.Loopback);
+ listen.Listen(1);
+
+ var cts = new CancellationTokenSource();
+ cts.Cancel();
+
+ var acceptTask = listen.AcceptAsync(cts.Token);
+ Assert.True(acceptTask.IsCompleted);
+
+ var oce = await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await acceptTask);
+ Assert.Equal(cts.Token, oce.CancellationToken);
+ }
+ }
+
+ [Fact]
+ public async Task AcceptAsync_CanceledDuringOperation_Throws()
+ {
+ using (Socket listen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
+ {
+ int port = listen.BindToAnonymousPort(IPAddress.Loopback);
+ listen.Listen(1);
+
+ var cts = new CancellationTokenSource();
+
+ var acceptTask = listen.AcceptAsync(cts.Token);
+ Assert.False(acceptTask.IsCompleted);
+
+ cts.Cancel();
+
+ var oce = await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await acceptTask);
+ Assert.Equal(cts.Token, oce.CancellationToken);
+ }
+ }
+ }
+
public sealed class AcceptEap : Accept<SocketHelperEap>
{
public AcceptEap(ITestOutputHelper output) : base(output) {}
public sealed class SendFile_Task : SendFile<SocketHelperTask>
{
public SendFile_Task(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class SendFile_CancellableTask : SendFile<SocketHelperCancellableTask>
+ {
+ public SendFile_CancellableTask(ITestOutputHelper output) : base(output) { }
[Fact]
- public async Task Precanceled_Throws()
+ public async Task SendFileAsync_Precanceled_Throws()
{
using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
[Theory]
[InlineData(false)]
[InlineData(true)]
- public async Task SendAsync_CanceledDuringOperation_Throws(bool ipv6)
+ public async Task SendFileAsync_CanceledDuringOperation_Throws(bool ipv6)
{
const int CancelAfter = 200; // ms
const int NumOfSends = 100;
public override bool ValidatesArrayArguments => false;
public override Task<Socket> AcceptAsync(Socket s) =>
- s.AcceptAsync();
+ s.AcceptAsync(_cts.Token).AsTask();
public override Task<(Socket socket, byte[] buffer)> AcceptAsync(Socket s, int receiveSize)
=> throw new NotSupportedException();
public override Task<Socket> AcceptAsync(Socket s, Socket acceptSocket) =>
- s.AcceptAsync(acceptSocket);
+ s.AcceptAsync(acceptSocket, _cts.Token).AsTask();
public override Task ConnectAsync(Socket s, EndPoint endPoint) =>
s.ConnectAsync(endPoint, _cts.Token).AsTask();
public override Task MultiConnectAsync(Socket s, IPAddress[] addresses, int port) =>
[Theory]
[InlineData(0)] // Sync
[InlineData(1)] // Async
- [InlineData(2)] // APM
+ [InlineData(2)] // Async with Cancellation
+ [InlineData(3)] // APM
[ActiveIssue("https://github.com/dotnet/runtime/issues/51392", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)]
public async Task Accept_AcceptsPendingSocketOrClient(int mode)
{
{
0 => listener.AcceptSocket(),
1 => await listener.AcceptSocketAsync(),
+ 2 => await listener.AcceptSocketAsync(CancellationToken.None),
_ => await Task.Factory.FromAsync(listener.BeginAcceptSocket, listener.EndAcceptSocket, null),
})
{
{
0 => listener.AcceptTcpClient(),
1 => await listener.AcceptTcpClientAsync(),
+ 2 => await listener.AcceptTcpClientAsync(CancellationToken.None),
_ => await Task.Factory.FromAsync(listener.BeginAcceptTcpClient, listener.EndAcceptTcpClient, null),
})
{