/// <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) { // Pull the relevant state off of the SAEA SocketError error = saea.SocketError; int bytesTransferred = saea.BytesTransferred; bool wrapExceptionsInIOExceptions = saea._wrapExceptionsInIOExceptions; // 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 <int> builder = saea.GetCompletionResponsibility(out responsibleForReturningToPool); if (responsibleForReturningToPool) { s.ReturnSocketAsyncEventArgs(saea, isReceive); } // Complete the builder/task with the results. if (error == SocketError.Success) { builder.SetResult(bytesTransferred); } else { builder.SetException(GetException(error, wrapExceptionsInIOExceptions)); } }
/// <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>Returns a <see cref="TaskSocketAsyncEventArgs{TResult}"/> instance for reuse.</summary> /// <param name="saea">The instance to return.</param> /// <param name="isReceive">true if this instance is used for receives; false if used for sends.</param> private void ReturnSocketAsyncEventArgs(TaskSocketAsyncEventArgs <int> saea, bool isReceive) { // 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._accessed = false; saea._builder = default; saea._wrapExceptionsInIOExceptions = false; // Write this instance back as a cached instance, only if there isn't currently one cached. ref TaskSocketAsyncEventArgs <int>?cache = ref isReceive ? ref _multiBufferReceiveEventArgs : ref _multiBufferSendEventArgs;
internal Task <Socket> AcceptAsync(Socket acceptSocket) { // Get any cached SocketAsyncEventArg we may have. TaskSocketAsyncEventArgs <Socket> saea = Interlocked.Exchange(ref LazyInitializer.EnsureInitialized(ref _cachedTaskEventArgs).Accept, s_rentedSocketSentinel); if (saea == s_rentedSocketSentinel) { // An instance was once created (or is currently being created elsewhere), but some other // concurrent operation is using it. Since we can store at most one, and since an individual // APM operation is less expensive than creating a new SAEA and using it only once, we simply // fall back to using an APM implementation. return(AcceptAsyncApm(acceptSocket)); } else if (saea == null) { // No instance has been created yet, so create one. saea = new TaskSocketAsyncEventArgs <Socket>(); saea.Completed += AcceptCompletedHandler; } // Configure the SAEA. saea.AcceptSocket = acceptSocket; // Initiate the accept operation. Task <Socket> t; if (AcceptAsync(saea)) { // 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); } } 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); } return(t); }
/// <summary>Gets a task to represent the operation.</summary> /// <param name="pending">true if the operation completes asynchronously; false if it completed synchronously.</param> /// <param name="saea">The event args instance used with the operation.</param> /// <param name="fromNetworkStream"> /// true if the request is coming from NetworkStream, which has special semantics for /// exceptions and cached tasks; otherwise, false. /// </param> /// <param name="isReceive">true if this is a receive; false if this is a send.</param> private Task <int> GetTaskForSendReceive(bool pending, TaskSocketAsyncEventArgs <int> saea, bool fromNetworkStream, bool isReceive) { Task <int> t; if (pending) { // 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, isReceive); } } else { // The operation completed synchronously. Get a task for it. if (saea.SocketError == SocketError.Success) { // Get the number of bytes successfully received/sent. int bytesTransferred = saea.BytesTransferred; // For zero bytes transferred, we can return our cached 0 task. // We can also do so if the request came from network stream and is a send, // as for that we can return any value because it returns a non-generic Task. if (bytesTransferred == 0 || (fromNetworkStream & !isReceive)) { t = s_zeroTask; } else { // Otherwise, create a new task for this result value. t = Task.FromResult(bytesTransferred); } } else { t = Task.FromException <int>(GetException(saea.SocketError, wrapExceptionsInIOExceptions: fromNetworkStream)); } // There won't be a callback, and we're done with the SAEA, so return it to the pool. ReturnSocketAsyncEventArgs(saea, isReceive); } return(t); }
/// <summary> /// Establishes a new connection for a request. /// </summary> /// <param name="message">The request causing this connection to be established. Once connected, it may be reused for many subsequent requests.</param> /// <param name="endPoint">The EndPoint to connect to.</param> /// <param name="options">Properties, if any, that might change how the connection is made.</param> /// <param name="cancellationToken">A cancellation token for the asynchronous operation.</param> /// <returns>A new open connection.</returns> public virtual async ValueTask <Connection> EstablishConnectionAsync(HttpRequestMessage message, EndPoint?endPoint, IConnectionProperties options, CancellationToken cancellationToken) { if (message == null) { throw new ArgumentNullException(nameof(message)); } if (endPoint == null) { throw new ArgumentNullException(nameof(endPoint)); } Socket socket = CreateSocket(message, endPoint, options); try { using var args = new TaskSocketAsyncEventArgs(); args.RemoteEndPoint = endPoint; if (socket.ConnectAsync(args)) { using (cancellationToken.UnsafeRegister(o => Socket.CancelConnectAsync((SocketAsyncEventArgs)o !), args)) { await args.Task.ConfigureAwait(false); } } if (args.SocketError != SocketError.Success) { Exception ex = args.SocketError == SocketError.OperationAborted && cancellationToken.IsCancellationRequested ? (Exception) new OperationCanceledException(cancellationToken) : new SocketException((int)args.SocketError); throw ex; } socket.NoDelay = true; return(new SocketConnection(socket)); } catch (SocketException socketException) { socket.Dispose(); throw NetworkErrorHelper.MapSocketException(socketException); } catch { socket.Dispose(); throw; } }
internal Task <int> SendAsync(IList <ArraySegment <byte> > buffers, SocketFlags socketFlags) { ValidateBuffersList(buffers); TaskSocketAsyncEventArgs <int>?saea = Interlocked.Exchange(ref _multiBufferSendEventArgs, null); if (saea is null) { saea = new TaskSocketAsyncEventArgs <int>(); saea.Completed += (s, e) => CompleteSendReceive((Socket)s !, (TaskSocketAsyncEventArgs <int>)e, isReceive: false); } saea.BufferList = buffers; saea.SocketFlags = socketFlags; return(GetTaskForSendReceive(SendAsync(saea), saea, fromNetworkStream: false, isReceive: false)); }
/// <summary>Returns a <see cref="Int32TaskSocketAsyncEventArgs"/> instance for reuse.</summary> /// <param name="saea">The instance to return.</param> /// <param name="isReceive">true if this instance is used for receives; false if used for sends.</param> private void ReturnSocketAsyncEventArgs(TaskSocketAsyncEventArgs <Socket> saea) { Debug.Assert(_cachedTaskEventArgs != null, "Should have been initialized when renting"); Debug.Assert(saea != s_rentedSocketSentinel); // 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(AsyncTaskMethodBuilder <Socket>); // Write this instance back as a cached instance. It should only ever be overwriting the sentinel, // never null or another instance. Debug.Assert(_cachedTaskEventArgs.Accept == s_rentedSocketSentinel); Volatile.Write(ref _cachedTaskEventArgs.Accept, saea); }
internal 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); } // Configure the SAEA. saea.AcceptSocket = acceptSocket; // Initiate the accept operation. Task <Socket> t; if (AcceptAsync(saea)) { // 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); } } 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); } return(t); }