File: TlsAwaitable.cs
Web Access
Project: src\aspnetcore\src\Servers\Kestrel\Transport.DirectTls\src\Microsoft.AspNetCore.Server.Kestrel.Transport.DirectTls.csproj (Microsoft.AspNetCore.Server.Kestrel.Transport.DirectTls)
// 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.Tasks.Sources;
 
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.DirectTls;
 
/// <summary>
/// A reusable awaitable that avoids allocating a <see cref="TaskCompletionSource"/> for each
/// async read/write. Wraps <see cref="ManualResetValueTaskSourceCore{TResult}"/> for the
/// continuation/result plumbing and adds a single lock-free "is a wait pending" gate.
/// </summary>
/// <remarks>
/// Three participants touch an instance, coordinated entirely through the single <c>_state</c>
/// word via <see cref="Interlocked"/>: the ThreadPool loop that arms it (<see cref="Reset"/>), the
/// epoll pump thread that completes a pending wait (<c>TrySet*</c>), and connection disposal, which
/// permanently <see cref="Cancel">cancels</see> it. The arming loop cannot advance past <c>await</c>
/// (and therefore cannot call <see cref="Reset"/> again) until a completion has run, so the pump and
/// the loop never touch <see cref="ManualResetValueTaskSourceCore{TResult}.Reset"/>/<c>Set*</c>
/// concurrently. <see cref="Cancel"/> may run at any time, but because it and every other transition
/// are <see cref="Interlocked"/> operations on <c>_state</c>, they are totally ordered on one word:
/// exactly one of them completes the underlying source for a given version, so arming and cancelling
/// can never both "miss" (the lost-cancel race a separate flag would leave open).
/// The state transition in <see cref="Reset"/> also publishes the buffers/flags the caller set
/// beforehand to the pump thread (release/acquire), which is the memory barrier the previous
/// <c>lock</c> provided.
/// </remarks>
internal sealed class TlsAwaitable<T> : IValueTaskSource<T>
{
    private const int Idle = 0;
    private const int Active = 1;
 
    // Sticky terminal state set by Cancel() on connection disposal. Once here the awaitable never
    // arms again: Reset() hands back an already-cancelled result instead of parking a wait that the
    // pump can no longer complete (its fd is being removed from epoll).
    private const int Canceled = 2;
 
    private ManualResetValueTaskSourceCore<T> _source;
    private int _state;
 
    public TlsAwaitable()
    {
        // RunContinuationsAsynchronously to avoid stack dives and deadlocks.
        // While this adds ThreadPool dispatch overhead, running inline caused crashes
        // under high concurrency (c=500) and didn't improve performance at c=100.
        _source.RunContinuationsAsynchronously = true;
    }
 
    /// <summary>
    /// Returns true if this awaitable is currently waiting for a result.
    /// </summary>
    public bool IsActive => Volatile.Read(ref _state) == Active;
 
    /// <summary>
    /// Prepares the awaitable for a new async wait and returns a <see cref="ValueTask{T}"/> to await.
    /// Called only by the consumer loop, and only after the previous wait has completed. If the
    /// awaitable has been <see cref="Cancel">canceled</see> (the connection is being disposed), this
    /// returns an already-canceled result instead of arming a wait the pump can no longer complete.
    /// </summary>
    public ValueTask<T> Reset()
    {
        if (Volatile.Read(ref _state) == Active)
        {
            throw new InvalidOperationException("TlsAwaitable is already active");
        }
 
        _source.Reset();
 
        // Idle -> Active arms the wait. This Interlocked full fence publishes _source.Reset() and any
        // buffers/flags the caller set beforehand to the pump thread (which observes Active via IsActive's
        // acquiring read). If the state is instead the sticky Canceled - because Cancel() already ran, or
        // wins the race with this CAS - the CAS is a no-op and we hand back an already-canceled ValueTask,
        // so the consumer loop unwinds rather than parking on an epoll event that will never arrive once
        // the fd is unregistered during disposal.
        if (Interlocked.CompareExchange(ref _state, Active, Idle) == Canceled)
        {
            _source.SetException(new OperationCanceledException());
        }
 
        return new ValueTask<T>(this, _source.Version);
    }
 
    /// <summary>
    /// Completes the awaitable with a successful result.
    /// Thread-safe: first caller wins, subsequent calls return false.
    /// </summary>
    public bool TrySetResult(T result)
    {
        if (Interlocked.CompareExchange(ref _state, Idle, Active) != Active)
        {
            return false;
        }
 
        _source.SetResult(result);
        return true;
    }
 
    /// <summary>
    /// Completes the awaitable with an exception.
    /// Thread-safe: first caller wins, subsequent calls return false.
    /// </summary>
    public bool TrySetException(Exception exception)
    {
        if (Interlocked.CompareExchange(ref _state, Idle, Active) != Active)
        {
            return false;
        }
 
        _source.SetException(exception);
        return true;
    }
 
    /// <summary>
    /// Permanently cancels the awaitable. Any in-flight wait is completed with
    /// <see cref="OperationCanceledException"/>, and every subsequent <see cref="Reset"/> returns an
    /// already-cancelled result. Called once, from connection disposal, and coordinates with
    /// <see cref="Reset"/> and the pump's <c>TrySet*</c> through the single <c>_state</c> word so a
    /// wait armed concurrently with disposal is always completed rather than left parked forever.
    /// </summary>
    public void Cancel()
    {
        // Exchange to the sticky Canceled state. If a wait was pending (Active), we won the race against
        // the pump's TrySet* (whose Active-conditioned CAS now fails), so we are the sole completer of the
        // source. If it was Idle, the loop is between operations; its next Reset() observes Canceled and
        // returns a cancelled result. Either way the loop cannot re-arm and park after this point.
        if (Interlocked.Exchange(ref _state, Canceled) == Active)
        {
            _source.SetException(new OperationCanceledException());
        }
    }
 
    // IValueTaskSource<T> implementation
    public T GetResult(short token)
    {
        // We don't validate the token since we control all usage
        return _source.GetResult(token);
    }
 
    public ValueTaskSourceStatus GetStatus(short token)
    {
        return _source.GetStatus(token);
    }
 
    public void OnCompleted(Action<object?> continuation, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
    {
        _source.OnCompleted(continuation, state, token, flags);
    }
}