File: src\runtime\src\libraries\System.Private.CoreLib\src\System\Runtime\CompilerServices\AsyncHelpers.cs
Web Access
Project: System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;

namespace System.Runtime.CompilerServices
{
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    public static partial class AsyncHelpers
    {
#if CORECLR || NATIVEAOT
        // "BypassReadyToRun" is until AOT/R2R typesystem has support for MethodImpl.Async
        // Must be NoInlining because we use AsyncSuspend to manufacture an explicit suspension point.
        // It will not capture/restore any local state that is live across it.

        /// <summary>
        /// Awaits the specified awaiter and returns when the awaiter has completed.
        /// </summary>
        /// <typeparam name="TAwaiter">The awaiter type.</typeparam>
        /// <param name="awaiter">The awaiter to await.</param>
        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
        [StackTraceHidden]
        public static unsafe void AwaitAwaiter<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion
        {
            ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
            Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation();
            state.StackState->Notifier = awaiter;
            state.CaptureContexts();
            AsyncSuspend(sentinelContinuation);
        }

        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
        [StackTraceHidden]
        private static unsafe void AwaitAwaiterInContinuation<TAwaiter>(int offset)
            where TAwaiter : INotifyCompletion
        {
            ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
            Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation();
            state.StackState->AwaiterContinuation = &AwaiterOnCompletedFromContinuation<TAwaiter>;
            state.StackState->AwaiterOffset = offset;
            state.CaptureContexts();
            AsyncSuspend(sentinelContinuation);
        }

        private static void AwaiterOnCompletedFromContinuation<TAwaiter>(
            Continuation headContinuation, int offset, Action continuation)
            where TAwaiter : INotifyCompletion
        {
            ref byte data = ref RuntimeHelpers.GetRawData(headContinuation);
            TAwaiter awaiter = Unsafe.As<byte, TAwaiter>(ref Unsafe.Add(ref data, offset));
            awaiter.OnCompleted(continuation);
        }

        // Must be NoInlining because we use AsyncSuspend to manufacture an explicit suspension point.
        // It will not capture/restore any local state that is live across it.

        /// <summary>
        /// Awaits the specified awaiter without capturing the execution context and returns when the awaiter has completed.
        /// </summary>
        /// <typeparam name="TAwaiter">The awaiter type.</typeparam>
        /// <param name="awaiter">The awaiter to await.</param>
        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
        [StackTraceHidden]
        public static unsafe void UnsafeAwaitAwaiter<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion
        {
            ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
            Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation();

            // We optimize specially for "await Task.Yield()" -- in the same
            // way that YieldAwaiter implements IStateMachineBoxAwareAwaiter
            // for async1. This avoids allocating internal thread pool objects
            // for this case.
            if (typeof(TAwaiter) == typeof(YieldAwaitable.YieldAwaiter))
            {
                state.StackState->CriticalNotifier = RuntimeAsyncYielder.Instance;
            }
            else
            {
                state.StackState->CriticalNotifier = awaiter;
            }

            state.CaptureContexts();
            AsyncSuspend(sentinelContinuation);
        }

        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
        [StackTraceHidden]
        private static unsafe void UnsafeAwaitAwaiterInContinuation<TAwaiter>(int offset)
            where TAwaiter : ICriticalNotifyCompletion
        {
            ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
            Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation();
            state.StackState->AwaiterContinuation = &UnsafeAwaiterOnCompletedFromContinuation<TAwaiter>;
            state.StackState->AwaiterOffset = offset;
            state.CaptureContexts();
            AsyncSuspend(sentinelContinuation);
        }

        private static void UnsafeAwaiterOnCompletedFromContinuation<TAwaiter>(
            Continuation headContinuation, int offset, Action continuation)
            where TAwaiter : ICriticalNotifyCompletion
        {
            ref byte data = ref RuntimeHelpers.GetRawData(headContinuation);
            TAwaiter awaiter = Unsafe.As<byte, TAwaiter>(ref Unsafe.Add(ref data, offset));
            awaiter.UnsafeOnCompleted(continuation);
        }

        /// <summary>
        /// Awaits the specified <see cref="Task{T}"/> and returns its result, throwing any exception produced by the task.
        /// </summary>
        /// <typeparam name="T">The result type produced by the task.</typeparam>
        /// <param name="task">The task to await.</param>
        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.Async)]
        [StackTraceHidden]
        public static T Await<T>(Task<T> task)
        {
            if (!task.IsCompleted)
            {
                TailAwait();
                return Suspend(task, ConfigureAwaitOptions.ContinueOnCapturedContext);
            }

            TaskAwaiter.ValidateEnd(task);
            return task.ResultOnSuccess;
        }

        /// <summary>
        /// Awaits the specified <see cref="Task"/> and throws any exception produced by the operation.
        /// </summary>
        /// <param name="task">The task to await.</param>
        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.Async)]
        [StackTraceHidden]
        public static void Await(Task task)
        {
            if (!task.IsCompleted)
            {
                TailAwait();
                Suspend(task, ConfigureAwaitOptions.ContinueOnCapturedContext);
                return;
            }

            TaskAwaiter.ValidateEnd(task);
        }

        /// <summary>
        /// Awaits the specified <see cref="ValueTask{T}"/> and returns its result, throwing any exception produced by the operation.
        /// </summary>
        /// <typeparam name="T">The result type produced by the value task.</typeparam>
        /// <param name="task">The value task to await.</param>
        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.Async)]
        [StackTraceHidden]
        public static T Await<T>(ValueTask<T> task)
        {
            object? obj = task._obj;
            if (obj == null)
            {
                return task._result!;
            }

            if (obj is Task<T> t)
            {
                if (t.IsCompleted)
                {
                    TaskAwaiter.ValidateEnd(t);
                    return t.ResultOnSuccess;
                }

                TailAwait();
                return Suspend(t, ConfigureAwaitOptions.ContinueOnCapturedContext);
            }

            Debug.Assert(obj is IValueTaskSource<T>);
            IValueTaskSource<T> vts = Unsafe.As<object, IValueTaskSource<T>>(ref obj);
            if (vts.GetStatus(task._token) == ValueTaskSourceStatus.Pending)
            {
                TailAwait();
                return Suspend(vts, task._token, true);
            }

            return vts.GetResult(task._token);
        }

        /// <summary>
        /// Awaits the specified <see cref="ValueTask"/> and throws any exception produced by the operation.
        /// </summary>
        /// <param name="task">The value task to await.</param>
        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.Async)]
        [StackTraceHidden]
        public static void Await(ValueTask task)
        {
            object? obj = task._obj;
            if (obj == null)
            {
                return;
            }

            if (obj is Task t)
            {
                if (t.IsCompleted)
                {
                    TaskAwaiter.ValidateEnd(t);
                    return;
                }

                TailAwait();
                Suspend(t, ConfigureAwaitOptions.ContinueOnCapturedContext);
                return;
            }

            Debug.Assert(obj is IValueTaskSource);
            IValueTaskSource vts = Unsafe.As<object, IValueTaskSource>(ref obj);
            if (vts.GetStatus(task._token) == ValueTaskSourceStatus.Pending)
            {
                TailAwait();
                Suspend(vts, task._token, true);
                return;
            }

            vts.GetResult(task._token);
        }

        /// <summary>
        /// Awaits the specified configured task awaitable without capturing the execution context and throws any exception produced by the operation.
        /// </summary>
        /// <param name="configuredAwaitable">The configured awaitable to await.</param>
        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.Async)]
        [StackTraceHidden]
        public static void Await(ConfiguredTaskAwaitable configuredAwaitable)
        {
            ConfiguredTaskAwaitable.ConfiguredTaskAwaiter awaiter = configuredAwaitable.GetAwaiter();
            if ((awaiter.m_options & ConfigureAwaitOptions.SuppressThrowing) != 0)
            {
                TailAwait();
                AwaitTaskWithRareOptions(awaiter);
                return;
            }

            if (!awaiter.IsCompleted)
            {
                TailAwait();
                Suspend(awaiter.m_task, awaiter.m_options);
                return;
            }

            awaiter.GetResult();
        }

        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.Async | MethodImplOptions.NoInlining)]
        [StackTraceHidden]
        private static void AwaitTaskWithRareOptions(ConfiguredTaskAwaitable.ConfiguredTaskAwaiter awaiter)
        {
            if (!awaiter.IsCompleted)
            {
                UnsafeAwaitAwaiter(awaiter);
            }

            awaiter.GetResult();
        }

        /// <summary>
        /// Awaits the specified configured value task awaitable without capturing the execution context and throws any exception produced by the operation.
        /// </summary>
        /// <param name="configuredAwaitable">The configured value task awaitable to await.</param>
        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.Async)]
        [StackTraceHidden]
        public static void Await(ConfiguredValueTaskAwaitable configuredAwaitable)
        {
            ValueTask task = configuredAwaitable._value;
            object? obj = task._obj;
            if (obj == null)
            {
                return;
            }

            if (obj is Task t)
            {
                if (t.IsCompleted)
                {
                    TaskAwaiter.ValidateEnd(t);
                    return;
                }

                TailAwait();
                Suspend(t, task._continueOnCapturedContext ? ConfigureAwaitOptions.ContinueOnCapturedContext : ConfigureAwaitOptions.None);
                return;
            }

            Debug.Assert(obj is IValueTaskSource);
            IValueTaskSource vts = Unsafe.As<object, IValueTaskSource>(ref obj);
            if (vts.GetStatus(task._token) == ValueTaskSourceStatus.Pending)
            {
                TailAwait();
                Suspend(vts, task._token, task._continueOnCapturedContext);
                return;
            }

            vts.GetResult(task._token);
        }

        /// <summary>
        /// Awaits the specified configured task awaitable and returns its result, throwing any exception produced by the operation.
        /// </summary>
        /// <typeparam name="T">The result type produced by the awaitable.</typeparam>
        /// <param name="configuredAwaitable">The configured awaitable to await.</param>
        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.Async)]
        [StackTraceHidden]
        public static T Await<T>(ConfiguredTaskAwaitable<T> configuredAwaitable)
        {
            ConfiguredTaskAwaitable<T>.ConfiguredTaskAwaiter awaiter = configuredAwaitable.GetAwaiter();
            // For the T version of ConfiguredTaskAwaiter we do not need to
            // support ConfigureAwaitOptions.SuppressThrowing, but we do need
            // to support ForceYielding. That one gets folded into the
            // awaiter's IsCompleted here.
            if (!awaiter.IsCompleted)
            {
                TailAwait();
                return Suspend(awaiter.m_task, awaiter.m_options);
            }

            return awaiter.GetResult();
        }

        /// <summary>
        /// Awaits the specified configured value task awaitable and returns its result, throwing any exception produced by the operation.
        /// </summary>
        /// <typeparam name="T">The result type produced by the awaitable.</typeparam>
        /// <param name="configuredAwaitable">The configured awaitable to await.</param>
        [Intrinsic]
        [BypassReadyToRun]
        [MethodImpl(MethodImplOptions.Async)]
        [StackTraceHidden]
        public static T Await<T>(ConfiguredValueTaskAwaitable<T> configuredAwaitable)
        {
            ValueTask<T> task = configuredAwaitable._value;
            object? obj = task._obj;
            if (obj == null)
            {
                return task._result!;
            }

            if (obj is Task<T> t)
            {
                if (t.IsCompleted)
                {
                    TaskAwaiter.ValidateEnd(t);
                    return t.ResultOnSuccess;
                }

                TailAwait();
                return Suspend(t, task._continueOnCapturedContext ? ConfigureAwaitOptions.ContinueOnCapturedContext : ConfigureAwaitOptions.None);
            }

            Debug.Assert(obj is IValueTaskSource<T>);
            IValueTaskSource<T> vts = Unsafe.As<object, IValueTaskSource<T>>(ref obj);
            if (vts.GetStatus(task._token) == ValueTaskSourceStatus.Pending)
            {
                TailAwait();
                return Suspend(vts, task._token, task._continueOnCapturedContext);
            }

            return vts.GetResult(task._token);
        }
#else
        public static void UnsafeAwaitAwaiter<TAwaiter>(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion { throw new PlatformNotSupportedException("Runtime Async is not supported on this platform."); }
        public static void AwaitAwaiter<TAwaiter>(TAwaiter awaiter) where TAwaiter : INotifyCompletion { throw new PlatformNotSupportedException("Runtime Async is not supported on this platform."); }
        public static void Await(System.Threading.Tasks.Task task) { throw new PlatformNotSupportedException("Runtime Async is not supported on this platform."); }
        public static T Await<T>(System.Threading.Tasks.Task<T> task) { throw new PlatformNotSupportedException("Runtime Async is not supported on this platform."); }
        public static void Await(System.Threading.Tasks.ValueTask task) { throw new PlatformNotSupportedException("Runtime Async is not supported on this platform."); }
        public static T Await<T>(System.Threading.Tasks.ValueTask<T> task) { throw new PlatformNotSupportedException("Runtime Async is not supported on this platform."); }
        public static void Await(System.Runtime.CompilerServices.ConfiguredTaskAwaitable configuredAwaitable) { throw new PlatformNotSupportedException("Runtime Async is not supported on this platform."); }
        public static void Await(System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable configuredAwaitable) { throw new PlatformNotSupportedException("Runtime Async is not supported on this platform."); }
        public static T Await<T>(System.Runtime.CompilerServices.ConfiguredTaskAwaitable<T> configuredAwaitable) { throw new PlatformNotSupportedException("Runtime Async is not supported on this platform."); }
        public static T Await<T>(System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable<T> configuredAwaitable) { throw new PlatformNotSupportedException("Runtime Async is not supported on this platform."); }
#endif
    }
}