#pragma warning disable #if !NET // Copied verbatim from https://github.com/dotnet/runtime/blob/261611930d6b436d7c4395450356b624d903d9bf/src/libraries/Common/src/System/Threading/Tasks/TaskToAsyncResult.cs // 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; namespace System.Threading.Tasks { /// /// Provides methods for using to implement the Asynchronous Programming Model /// pattern based on "Begin" and "End" methods. /// #if SYSTEM_PRIVATE_CORELIB public #else internal #endif static class TaskToAsyncResult { /// Creates a new from the specified , optionally invoking when the task has completed. /// The to be wrapped in an . /// The callback to be invoked upon 's completion. If , no callback will be invoked. /// The state to be stored in the . /// An to represent the task's asynchronous operation. This instance will also be passed to when it's invoked. /// is null. /// /// In conjunction with the or methods, this method may be used /// to implement the Begin/End pattern (also known as the Asynchronous Programming Model pattern, or APM). It is recommended to not expose this pattern /// in new code; the methods on are intended only to help implement such Begin/End methods when they must be exposed, for example /// because a base class provides virtual methods for the pattern, or when they've already been exposed and must remain for compatibility. These methods enable /// implementing all of the core asynchronous logic via s and then easily implementing Begin/End methods around that functionality. /// public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state) { #if NET ArgumentNullException.ThrowIfNull(task); #else if (task is null) { throw new ArgumentNullException(nameof(task)); } #endif return new TaskAsyncResult(task, state, callback); } /// Waits for the wrapped by the returned by to complete. /// The for which to wait. /// is null. /// was not produced by a call to . /// This will propagate any exception stored in the wrapped . public static void End(IAsyncResult asyncResult) => Unwrap(asyncResult).GetAwaiter().GetResult(); /// Waits for the wrapped by the returned by to complete. /// The type of the result produced. /// The for which to wait. /// The result of the wrapped by the . /// is null. /// was not produced by a call to . /// This will propagate any exception stored in the wrapped . public static TResult End(IAsyncResult asyncResult) => Unwrap(asyncResult).GetAwaiter().GetResult(); /// Extracts the underlying from an created by . /// The created by . /// The wrapped by the . /// is null. /// was not produced by a call to . public static Task Unwrap(IAsyncResult asyncResult) { #if NET ArgumentNullException.ThrowIfNull(asyncResult); #else if (asyncResult is null) { throw new ArgumentNullException(nameof(asyncResult)); } #endif if ((asyncResult as TaskAsyncResult)?._task is not Task task) { throw new ArgumentException(null, nameof(asyncResult)); } return task; } /// Extracts the underlying from an created by . /// The type of the result produced by the returned task. /// The created by . /// The wrapped by the . /// is null. /// /// was not produced by a call to , /// or the provided to was used a generic type parameter /// that's different from the supplied to this call. /// public static Task Unwrap(IAsyncResult asyncResult) { #if NET ArgumentNullException.ThrowIfNull(asyncResult); #else if (asyncResult is null) { throw new ArgumentNullException(nameof(asyncResult)); } #endif if ((asyncResult as TaskAsyncResult)?._task is not Task task) { throw new ArgumentException(null, nameof(asyncResult)); } return task; } /// Provides a simple that wraps a . /// /// We could use the Task as the IAsyncResult if the Task's AsyncState is the same as the object state, /// but that's very rare, in particular in a situation where someone cares about allocation, and always /// using TaskAsyncResult simplifies things and enables additional optimizations. /// private sealed class TaskAsyncResult : IAsyncResult { /// The wrapped Task. internal readonly Task _task; /// Callback to invoke when the wrapped task completes. private readonly AsyncCallback? _callback; /// Initializes the IAsyncResult with the Task to wrap and the associated object state. /// The Task to wrap. /// The new AsyncState value. /// Callback to invoke when the wrapped task completes. internal TaskAsyncResult(Task task, object? state, AsyncCallback? callback) { Debug.Assert(task is not null); _task = task; AsyncState = state; if (task.IsCompleted) { // The task has already completed. Treat this as synchronous completion. // Invoke the callback; no need to store it. CompletedSynchronously = true; callback?.Invoke(this); } else if (callback is not null) { // Asynchronous completion, and we have a callback; schedule it. We use OnCompleted rather than ContinueWith in // order to avoid running synchronously if the task has already completed by the time we get here but still run // synchronously as part of the task's completion if the task completes after (the more common case). _callback = callback; _task.ConfigureAwait(continueOnCapturedContext: false) .GetAwaiter() .OnCompleted(() => _callback.Invoke(this)); } } /// public object? AsyncState { get; } /// public bool CompletedSynchronously { get; } /// public bool IsCompleted => _task.IsCompleted; /// public WaitHandle AsyncWaitHandle => ((IAsyncResult)_task).AsyncWaitHandle; } } } #endif