#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