copy some Task-related files from corefx-mono-2018-06

This commit is contained in:
EgorBo 2018-11-20 15:50:42 +03:00 коммит произвёл Marek Safar
Родитель 1e8083be27
Коммит 871e5d71e7
4 изменённых файлов: 1271 добавлений и 168 удалений

Просмотреть файл

@ -8,6 +8,108 @@ using System.Threading.Tasks;
namespace System.Runtime.CompilerServices
{
/// <summary>Represents a builder for asynchronous methods that return a <see cref="ValueTask"/>.</summary>
[StructLayout(LayoutKind.Auto)]
public struct AsyncValueTaskMethodBuilder
{
/// <summary>The <see cref="AsyncTaskMethodBuilder"/> to which most operations are delegated.</summary>
private AsyncTaskMethodBuilder _methodBuilder; // mutable struct; do not make it readonly
/// <summary>true if completed synchronously and successfully; otherwise, false.</summary>
private bool _haveResult;
/// <summary>true if the builder should be used for setting/getting the result; otherwise, false.</summary>
private bool _useBuilder;
/// <summary>Creates an instance of the <see cref="AsyncValueTaskMethodBuilder"/> struct.</summary>
/// <returns>The initialized instance.</returns>
public static AsyncValueTaskMethodBuilder Create() =>
#if CORERT
// corert's AsyncTaskMethodBuilder.Create() currently does additional debugger-related
// work, so we need to delegate to it.
new AsyncValueTaskMethodBuilder() { _methodBuilder = AsyncTaskMethodBuilder.Create() };
#else
// _methodBuilder should be initialized to AsyncTaskMethodBuilder.Create(), but on coreclr
// that Create() is a nop, so we can just return the default here.
default;
#endif
/// <summary>Begins running the builder with the associated state machine.</summary>
/// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
/// <param name="stateMachine">The state machine instance, passed by reference.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine =>
// will provide the right ExecutionContext semantics
#if netstandard || MONO
_methodBuilder.Start(ref stateMachine);
#else
AsyncMethodBuilderCore.Start(ref stateMachine);
#endif
/// <summary>Associates the builder with the specified state machine.</summary>
/// <param name="stateMachine">The state machine instance to associate with the builder.</param>
public void SetStateMachine(IAsyncStateMachine stateMachine) => _methodBuilder.SetStateMachine(stateMachine);
/// <summary>Marks the task as successfully completed.</summary>
public void SetResult()
{
if (_useBuilder)
{
_methodBuilder.SetResult();
}
else
{
_haveResult = true;
}
}
/// <summary>Marks the task as failed and binds the specified exception to the task.</summary>
/// <param name="exception">The exception to bind to the task.</param>
public void SetException(Exception exception) => _methodBuilder.SetException(exception);
/// <summary>Gets the task for this builder.</summary>
public ValueTask Task
{
get
{
if (_haveResult)
{
return default;
}
else
{
_useBuilder = true;
return new ValueTask(_methodBuilder.Task);
}
}
}
/// <summary>Schedules the state machine to proceed to the next action when the specified awaiter completes.</summary>
/// <typeparam name="TAwaiter">The type of the awaiter.</typeparam>
/// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
/// <param name="awaiter">The awaiter.</param>
/// <param name="stateMachine">The state machine.</param>
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine
{
_useBuilder = true;
_methodBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
}
/// <summary>Schedules the state machine to proceed to the next action when the specified awaiter completes.</summary>
/// <typeparam name="TAwaiter">The type of the awaiter.</typeparam>
/// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
/// <param name="awaiter">The awaiter.</param>
/// <param name="stateMachine">The state machine.</param>
[SecuritySafeCritical]
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine
{
_useBuilder = true;
_methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
}
}
/// <summary>Represents a builder for asynchronous methods that returns a <see cref="ValueTask{TResult}"/>.</summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
[StructLayout(LayoutKind.Auto)]
@ -32,14 +134,20 @@ namespace System.Runtime.CompilerServices
#else
// _methodBuilder should be initialized to AsyncTaskMethodBuilder<TResult>.Create(), but on coreclr
// that Create() is a nop, so we can just return the default here.
default(AsyncValueTaskMethodBuilder<TResult>);
default;
#endif
/// <summary>Begins running the builder with the associated state machine.</summary>
/// <typeparam name="TStateMachine">The type of the state machine.</typeparam>
/// <param name="stateMachine">The state machine instance, passed by reference.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine =>
_methodBuilder.Start(ref stateMachine); // will provide the right ExecutionContext semantics
// will provide the right ExecutionContext semantics
#if netstandard || MONO
_methodBuilder.Start(ref stateMachine);
#else
AsyncMethodBuilderCore.Start(ref stateMachine);
#endif
/// <summary>Associates the builder with the specified state machine.</summary>
/// <param name="stateMachine">The state machine instance to associate with the builder.</param>
@ -108,4 +216,4 @@ namespace System.Runtime.CompilerServices
_methodBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
}
}
}
}

Просмотреть файл

@ -3,11 +3,127 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Diagnostics.Private;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
#if !netstandard
using Internal.Runtime.CompilerServices;
#endif
namespace System.Runtime.CompilerServices
{
/// <summary>Provides an awaitable type that enables configured awaits on a <see cref="ValueTask"/>.</summary>
[StructLayout(LayoutKind.Auto)]
public readonly struct ConfiguredValueTaskAwaitable
{
/// <summary>The wrapped <see cref="Task"/>.</summary>
private readonly ValueTask _value;
/// <summary>Initializes the awaitable.</summary>
/// <param name="value">The wrapped <see cref="ValueTask"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ConfiguredValueTaskAwaitable(ValueTask value) => _value = value;
/// <summary>Returns an awaiter for this <see cref="ConfiguredValueTaskAwaitable"/> instance.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ConfiguredValueTaskAwaiter GetAwaiter() => new ConfiguredValueTaskAwaiter(_value);
/// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable"/>.</summary>
[StructLayout(LayoutKind.Auto)]
public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion
#if CORECLR
, IStateMachineBoxAwareAwaiter
#endif
{
/// <summary>The value being awaited.</summary>
private readonly ValueTask _value;
/// <summary>Initializes the awaiter.</summary>
/// <param name="value">The value to be awaited.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ConfiguredValueTaskAwaiter(ValueTask value) => _value = value;
/// <summary>Gets whether the <see cref="ConfiguredValueTaskAwaitable"/> has completed.</summary>
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _value.IsCompleted;
}
/// <summary>Gets the result of the ValueTask.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[StackTraceHidden]
public void GetResult() => _value.ThrowIfCompletedUnsuccessfully();
/// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable"/>.</summary>
public void OnCompleted(Action continuation)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj is Task t)
{
t.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token,
ValueTaskSourceOnCompletedFlags.FlowExecutionContext |
(_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None));
}
else
{
ValueTask.CompletedTask.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
}
}
/// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable"/>.</summary>
public void UnsafeOnCompleted(Action continuation)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj is Task t)
{
t.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token,
_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
}
else
{
ValueTask.CompletedTask.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
}
}
#if CORECLR
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj is Task t)
{
TaskAwaiter.UnsafeOnCompletedInternal(t, box, _value._continueOnCapturedContext);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token,
_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
}
else
{
TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, _value._continueOnCapturedContext);
}
}
#endif
}
}
/// <summary>Provides an awaitable type that enables configured awaits on a <see cref="ValueTask{TResult}"/>.</summary>
/// <typeparam name="TResult">The type of the result produced.</typeparam>
[StructLayout(LayoutKind.Auto)]
@ -15,74 +131,107 @@ namespace System.Runtime.CompilerServices
{
/// <summary>The wrapped <see cref="ValueTask{TResult}"/>.</summary>
private readonly ValueTask<TResult> _value;
/// <summary>true to attempt to marshal the continuation back to the original context captured; otherwise, false.</summary>
private readonly bool _continueOnCapturedContext;
/// <summary>Initializes the awaitable.</summary>
/// <param name="value">The wrapped <see cref="ValueTask{TResult}"/>.</param>
/// <param name="continueOnCapturedContext">
/// true to attempt to marshal the continuation back to the original synchronization context captured; otherwise, false.
/// </param>
internal ConfiguredValueTaskAwaitable(ValueTask<TResult> value, bool continueOnCapturedContext)
{
_value = value;
_continueOnCapturedContext = continueOnCapturedContext;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ConfiguredValueTaskAwaitable(ValueTask<TResult> value) => _value = value;
/// <summary>Returns an awaiter for this <see cref="ConfiguredValueTaskAwaitable{TResult}"/> instance.</summary>
public ConfiguredValueTaskAwaiter GetAwaiter() =>
new ConfiguredValueTaskAwaiter(_value, _continueOnCapturedContext);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ConfiguredValueTaskAwaiter GetAwaiter() => new ConfiguredValueTaskAwaiter(_value);
/// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
[StructLayout(LayoutKind.Auto)]
public struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion, IConfiguredValueTaskAwaiter
public readonly struct ConfiguredValueTaskAwaiter : ICriticalNotifyCompletion
#if CORECLR
, IStateMachineBoxAwareAwaiter
#endif
{
/// <summary>The value being awaited.</summary>
private ValueTask<TResult> _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies
/// <summary>The value to pass to ConfigureAwait.</summary>
internal readonly bool _continueOnCapturedContext;
private readonly ValueTask<TResult> _value;
/// <summary>Initializes the awaiter.</summary>
/// <param name="value">The value to be awaited.</param>
/// <param name="continueOnCapturedContext">The value to pass to ConfigureAwait.</param>
internal ConfiguredValueTaskAwaiter(ValueTask<TResult> value, bool continueOnCapturedContext)
{
_value = value;
_continueOnCapturedContext = continueOnCapturedContext;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ConfiguredValueTaskAwaiter(ValueTask<TResult> value) => _value = value;
/// <summary>Gets whether the <see cref="ConfiguredValueTaskAwaitable{TResult}"/> has completed.</summary>
public bool IsCompleted => _value.IsCompleted;
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _value.IsCompleted;
}
/// <summary>Gets the result of the ValueTask.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[StackTraceHidden]
public TResult GetResult() =>
_value._task == null ?
_value._result :
_value._task.GetAwaiter().GetResult();
public TResult GetResult() => _value.Result;
/// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
public void OnCompleted(Action continuation) =>
_value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
public void OnCompleted(Action continuation)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
if (obj is Task<TResult> t)
{
t.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token,
ValueTaskSourceOnCompletedFlags.FlowExecutionContext |
(_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None));
}
else
{
ValueTask.CompletedTask.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().OnCompleted(continuation);
}
}
/// <summary>Schedules the continuation action for the <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
public void UnsafeOnCompleted(Action continuation) =>
_value.AsTask().ConfigureAwait(_continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
public void UnsafeOnCompleted(Action continuation)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
/// <summary>Gets the task underlying <see cref="_value"/>.</summary>
internal Task<TResult> AsTask() => _value.AsTask();
if (obj is Task<TResult> t)
{
t.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token,
_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
}
else
{
ValueTask.CompletedTask.ConfigureAwait(_value._continueOnCapturedContext).GetAwaiter().UnsafeOnCompleted(continuation);
}
}
/// <summary>Gets the task underlying the incomplete <see cref="_value"/>.</summary>
/// <remarks>This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task.</remarks>
(Task task, bool continueOnCapturedContext) IConfiguredValueTaskAwaiter.GetTask() => (_value.AsTaskExpectNonNull(), _continueOnCapturedContext);
#if CORECLR
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
if (obj is Task<TResult> t)
{
TaskAwaiter.UnsafeOnCompletedInternal(t, box, _value._continueOnCapturedContext);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token,
_value._continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None);
}
else
{
TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, _value._continueOnCapturedContext);
}
}
#endif
}
}
/// <summary>
/// Internal interface used to enable extract the Task from arbitrary configured ValueTask awaiters.
/// </summary>
internal interface IConfiguredValueTaskAwaiter
{
(Task task, bool continueOnCapturedContext) GetTask();
}
}
}

Просмотреть файл

@ -3,51 +3,222 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Diagnostics.Private;
using System.Threading.Tasks;
using System.Threading.Tasks.Sources;
#if !netstandard
using Internal.Runtime.CompilerServices;
#endif
namespace System.Runtime.CompilerServices
{
/// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary>
public struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion, IValueTaskAwaiter
/// <summary>Provides an awaiter for a <see cref="ValueTask"/>.</summary>
public readonly struct ValueTaskAwaiter : ICriticalNotifyCompletion
#if CORECLR
, IStateMachineBoxAwareAwaiter
#endif
{
/// <summary>Shim used to invoke an <see cref="Action"/> passed as the state argument to a <see cref="Action{Object}"/>.</summary>
internal static readonly Action<object> s_invokeActionDelegate = state =>
{
if (!(state is Action action))
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
return;
}
action();
};
/// <summary>The value being awaited.</summary>
private ValueTask<TResult> _value; // Methods are called on this; avoid making it readonly so as to avoid unnecessary copies
private readonly ValueTask _value;
/// <summary>Initializes the awaiter.</summary>
/// <param name="value">The value to be awaited.</param>
internal ValueTaskAwaiter(ValueTask<TResult> value) => _value = value;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ValueTaskAwaiter(ValueTask value) => _value = value;
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> has completed.</summary>
public bool IsCompleted => _value.IsCompleted;
/// <summary>Gets whether the <see cref="ValueTask"/> has completed.</summary>
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _value.IsCompleted;
}
/// <summary>Gets the result of the ValueTask.</summary>
[StackTraceHidden]
public TResult GetResult() =>
_value._task == null ?
_value._result :
_value._task.GetAwaiter().GetResult();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void GetResult() => _value.ThrowIfCompletedUnsuccessfully();
/// <summary>Schedules the continuation action for this ValueTask.</summary>
public void OnCompleted(Action continuation) =>
_value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().OnCompleted(continuation);
public void OnCompleted(Action continuation)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj is Task t)
{
t.GetAwaiter().OnCompleted(continuation);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource>(obj).OnCompleted(s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext | ValueTaskSourceOnCompletedFlags.FlowExecutionContext);
}
else
{
ValueTask.CompletedTask.GetAwaiter().OnCompleted(continuation);
}
}
/// <summary>Schedules the continuation action for this ValueTask.</summary>
public void UnsafeOnCompleted(Action continuation) =>
_value.AsTask().ConfigureAwait(continueOnCapturedContext: true).GetAwaiter().UnsafeOnCompleted(continuation);
public void UnsafeOnCompleted(Action continuation)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
/// <summary>Gets the task underlying <see cref="_value"/>.</summary>
internal Task<TResult> AsTask() => _value.AsTask();
if (obj is Task t)
{
t.GetAwaiter().UnsafeOnCompleted(continuation);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource>(obj).OnCompleted(s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
}
else
{
ValueTask.CompletedTask.GetAwaiter().UnsafeOnCompleted(continuation);
}
}
/// <summary>Gets the task underlying the incomplete <see cref="_value"/>.</summary>
/// <remarks>This method is used when awaiting and IsCompleted returned false; thus we expect the value task to be wrapping a non-null task.</remarks>
Task IValueTaskAwaiter.GetTask() => _value.AsTaskExpectNonNull();
#if CORECLR
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj is Task t)
{
TaskAwaiter.UnsafeOnCompletedInternal(t, box, continueOnCapturedContext: true);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource>(obj).OnCompleted(s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
}
else
{
TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, continueOnCapturedContext: true);
}
}
/// <summary>Shim used to invoke <see cref="ITaskCompletionAction.Invoke"/> of the supplied <see cref="IAsyncStateMachineBox"/>.</summary>
internal static readonly Action<object> s_invokeAsyncStateMachineBox = state =>
{
if (!(state is IAsyncStateMachineBox box))
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
return;
}
box.MoveNext();
};
#endif
}
/// <summary>
/// Internal interface used to enable extract the Task from arbitrary ValueTask awaiters.
/// </summary>>
internal interface IValueTaskAwaiter
/// <summary>Provides an awaiter for a <see cref="ValueTask{TResult}"/>.</summary>
public readonly struct ValueTaskAwaiter<TResult> : ICriticalNotifyCompletion
#if CORECLR
, IStateMachineBoxAwareAwaiter
#endif
{
Task GetTask();
/// <summary>The value being awaited.</summary>
private readonly ValueTask<TResult> _value;
/// <summary>Initializes the awaiter.</summary>
/// <param name="value">The value to be awaited.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ValueTaskAwaiter(ValueTask<TResult> value) => _value = value;
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> has completed.</summary>
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _value.IsCompleted;
}
/// <summary>Gets the result of the ValueTask.</summary>
[StackTraceHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TResult GetResult() => _value.Result;
/// <summary>Schedules the continuation action for this ValueTask.</summary>
public void OnCompleted(Action continuation)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
if (obj is Task<TResult> t)
{
t.GetAwaiter().OnCompleted(continuation);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext | ValueTaskSourceOnCompletedFlags.FlowExecutionContext);
}
else
{
ValueTask.CompletedTask.GetAwaiter().OnCompleted(continuation);
}
}
/// <summary>Schedules the continuation action for this ValueTask.</summary>
public void UnsafeOnCompleted(Action continuation)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
if (obj is Task<TResult> t)
{
t.GetAwaiter().UnsafeOnCompleted(continuation);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
}
else
{
ValueTask.CompletedTask.GetAwaiter().UnsafeOnCompleted(continuation);
}
}
#if CORECLR
void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box)
{
object obj = _value._obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
if (obj is Task<TResult> t)
{
TaskAwaiter.UnsafeOnCompletedInternal(t, box, continueOnCapturedContext: true);
}
else if (obj != null)
{
Unsafe.As<IValueTaskSource<TResult>>(obj).OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext);
}
else
{
TaskAwaiter.UnsafeOnCompletedInternal(Task.CompletedTask, box, continueOnCapturedContext: true);
}
}
#endif
}
}
#if CORECLR
/// <summary>Internal interface used to enable optimizations from <see cref="AsyncTaskMethodBuilder"/>.</summary>>
internal interface IStateMachineBoxAwareAwaiter
{
/// <summary>Invoked to set <see cref="ITaskCompletionAction.Invoke"/> of the <paramref name="box"/> as the awaiter's continuation.</summary>
/// <param name="box">The box object.</param>
void AwaitUnsafeOnCompleted(IAsyncStateMachineBox box);
}
#endif
}

Просмотреть файл

@ -3,72 +3,454 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks.Sources;
#if MONO
using System.Diagnostics.Private;
#endif
#if !netstandard
using Internal.Runtime.CompilerServices;
#endif
namespace System.Threading.Tasks
{
/// <summary>
/// Provides a value type that wraps a <see cref="Task{TResult}"/> and a <typeparamref name="TResult"/>,
/// only one of which is used.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
// TYPE SAFETY WARNING:
// This code uses Unsafe.As to cast _obj. This is done in order to minimize the costs associated with
// casting _obj to a variety of different types that can be stored in a ValueTask, e.g. Task<TResult>
// vs IValueTaskSource<TResult>. Previous attempts at this were faulty due to using a separate field
// to store information about the type of the object in _obj; this is faulty because if the ValueTask
// is stored into a field, concurrent read/writes can result in tearing the _obj from the type information
// stored in a separate field. This means we can rely only on the _obj field to determine how to handle
// it. As such, the pattern employed is to copy _obj into a local obj, and then check it for null and
// type test against Task/Task<TResult>. Since the ValueTask can only be constructed with null, Task,
// or IValueTaskSource, we can then be confident in knowing that if it doesn't match one of those values,
// it must be an IValueTaskSource, and we can use Unsafe.As. This could be defeated by other unsafe means,
// like private reflection or using Unsafe.As manually, but at that point you're already doing things
// that can violate type safety; we only care about getting correct behaviors when using "safe" code.
// There are still other race conditions in user's code that can result in errors, but such errors don't
// cause ValueTask to violate type safety.
/// <summary>Provides an awaitable result of an asynchronous operation.</summary>
/// <remarks>
/// <para>
/// Methods may return an instance of this value type when it's likely that the result of their
/// operations will be available synchronously and when the method is expected to be invoked so
/// frequently that the cost of allocating a new <see cref="Task{TResult}"/> for each call will
/// be prohibitive.
/// </para>
/// <para>
/// There are tradeoffs to using a <see cref="ValueTask{TResult}"/> instead of a <see cref="Task{TResult}"/>.
/// For example, while a <see cref="ValueTask{TResult}"/> can help avoid an allocation in the case where the
/// successful result is available synchronously, it also contains two fields whereas a <see cref="Task{TResult}"/>
/// as a reference type is a single field. This means that a method call ends up returning two fields worth of
/// data instead of one, which is more data to copy. It also means that if a method that returns one of these
/// is awaited within an async method, the state machine for that async method will be larger due to needing
/// to store the struct that's two fields instead of a single reference.
/// </para>
/// <para>
/// Further, for uses other than consuming the result of an asynchronous operation via await,
/// <see cref="ValueTask{TResult}"/> can lead to a more convoluted programming model, which can in turn actually
/// lead to more allocations. For example, consider a method that could return either a <see cref="Task{TResult}"/>
/// with a cached task as a common result or a <see cref="ValueTask{TResult}"/>. If the consumer of the result
/// wants to use it as a <see cref="Task{TResult}"/>, such as to use with in methods like Task.WhenAll and Task.WhenAny,
/// the <see cref="ValueTask{TResult}"/> would first need to be converted into a <see cref="Task{TResult}"/> using
/// <see cref="ValueTask{TResult}.AsTask"/>, which leads to an allocation that would have been avoided if a cached
/// <see cref="Task{TResult}"/> had been used in the first place.
/// </para>
/// <para>
/// As such, the default choice for any asynchronous method should be to return a <see cref="Task"/> or
/// <see cref="Task{TResult}"/>. Only if performance analysis proves it worthwhile should a <see cref="ValueTask{TResult}"/>
/// be used instead of <see cref="Task{TResult}"/>. There is no non-generic version of <see cref="ValueTask{TResult}"/>
/// as the Task.CompletedTask property may be used to hand back a successfully completed singleton in the case where
/// a <see cref="Task"/>-returning method completes synchronously and successfully.
/// </para>
/// <see cref="ValueTask"/>s are meant to be directly awaited. To do more complicated operations with them, a <see cref="Task"/>
/// should be extracted using <see cref="AsTask"/>. Such operations might include caching an instance to be awaited later,
/// registering multiple continuations with a single operation, awaiting the same task multiple times, and using combinators over
/// multiple operations.
/// </remarks>
[AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder))]
[StructLayout(LayoutKind.Auto)]
public readonly struct ValueTask : IEquatable<ValueTask>
{
/// <summary>A task canceled using `new CancellationToken(true)`.</summary>
private static readonly Task s_canceledTask =
#if netstandard
Task.Delay(Timeout.Infinite, new CancellationToken(canceled: true));
#else
Task.FromCanceled(new CancellationToken(canceled: true));
#endif
/// <summary>A successfully completed task.</summary>
internal static Task CompletedTask
#if netstandard
{ get; } = Task.Delay(0);
#else
=> Task.CompletedTask;
#endif
/// <summary>null if representing a successful synchronous completion, otherwise a <see cref="Task"/> or a <see cref="IValueTaskSource"/>.</summary>
internal readonly object _obj;
/// <summary>Opaque value passed through to the <see cref="IValueTaskSource"/>.</summary>
internal readonly short _token;
/// <summary>true to continue on the capture context; otherwise, true.</summary>
/// <remarks>Stored in the <see cref="ValueTask"/> rather than in the configured awaiter to utilize otherwise padding space.</remarks>
internal readonly bool _continueOnCapturedContext;
// An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation.
/// <summary>Initialize the <see cref="ValueTask"/> with a <see cref="Task"/> that represents the operation.</summary>
/// <param name="task">The task.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(Task task)
{
if (task == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task);
}
_obj = task;
_continueOnCapturedContext = true;
_token = 0;
}
/// <summary>Initialize the <see cref="ValueTask"/> with a <see cref="IValueTaskSource"/> object that represents the operation.</summary>
/// <param name="source">The source.</param>
/// <param name="token">Opaque value passed through to the <see cref="IValueTaskSource"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(IValueTaskSource source, short token)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
_obj = source;
_token = token;
_continueOnCapturedContext = true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ValueTask(object obj, short token, bool continueOnCapturedContext)
{
_obj = obj;
_token = token;
_continueOnCapturedContext = continueOnCapturedContext;
}
/// <summary>Returns the hash code for this instance.</summary>
public override int GetHashCode() => _obj?.GetHashCode() ?? 0;
/// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary>
public override bool Equals(object obj) =>
obj is ValueTask &&
Equals((ValueTask)obj);
/// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask"/> value.</summary>
public bool Equals(ValueTask other) => _obj == other._obj && _token == other._token;
/// <summary>Returns a value indicating whether two <see cref="ValueTask"/> values are equal.</summary>
public static bool operator ==(ValueTask left, ValueTask right) =>
left.Equals(right);
/// <summary>Returns a value indicating whether two <see cref="ValueTask"/> values are not equal.</summary>
public static bool operator !=(ValueTask left, ValueTask right) =>
!left.Equals(right);
/// <summary>
/// Gets a <see cref="Task"/> object to represent this ValueTask.
/// </summary>
/// <remarks>
/// It will either return the wrapped task object if one exists, or it'll
/// manufacture a new task object to represent the result.
/// </remarks>
public Task AsTask()
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
return
obj == null ? CompletedTask :
obj as Task ??
GetTaskForValueTaskSource(Unsafe.As<IValueTaskSource>(obj));
}
/// <summary>Gets a <see cref="ValueTask"/> that may be used at any point in the future.</summary>
public ValueTask Preserve() => _obj == null ? this : new ValueTask(AsTask());
/// <summary>Creates a <see cref="Task"/> to represent the <see cref="IValueTaskSource"/>.</summary>
/// <remarks>
/// The <see cref="IValueTaskSource"/> is passed in rather than reading and casting <see cref="_obj"/>
/// so that the caller can pass in an object it's already validated.
/// </remarks>
private Task GetTaskForValueTaskSource(IValueTaskSource t)
{
ValueTaskSourceStatus status = t.GetStatus(_token);
if (status != ValueTaskSourceStatus.Pending)
{
try
{
// Propagate any exceptions that may have occurred, then return
// an already successfully completed task.
t.GetResult(_token);
return CompletedTask;
// If status is Faulted or Canceled, GetResult should throw. But
// we can't guarantee every implementation will do the "right thing".
// If it doesn't throw, we just treat that as success and ignore
// the status.
}
catch (Exception exc)
{
if (status == ValueTaskSourceStatus.Canceled)
{
#if !netstandard
if (exc is OperationCanceledException oce)
{
var task = new Task<VoidTaskResult>();
task.TrySetCanceled(oce.CancellationToken, oce);
return task;
}
#endif
return s_canceledTask;
}
else
{
#if netstandard
var tcs = new TaskCompletionSource<bool>();
tcs.TrySetException(exc);
return tcs.Task;
#else
return Task.FromException(exc);
#endif
}
}
}
var m = new ValueTaskSourceAsTask(t, _token);
return
#if netstandard
m.Task;
#else
m;
#endif
}
/// <summary>Type used to create a <see cref="Task"/> to represent a <see cref="IValueTaskSource"/>.</summary>
private sealed class ValueTaskSourceAsTask :
#if netstandard
TaskCompletionSource<bool>
#else
Task<VoidTaskResult>
#endif
{
private static readonly Action<object> s_completionAction = state =>
{
if (!(state is ValueTaskSourceAsTask vtst) ||
!(vtst._source is IValueTaskSource source))
{
// This could only happen if the IValueTaskSource passed the wrong state
// or if this callback were invoked multiple times such that the state
// was previously nulled out.
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
return;
}
vtst._source = null;
ValueTaskSourceStatus status = source.GetStatus(vtst._token);
try
{
source.GetResult(vtst._token);
vtst.TrySetResult(default);
}
catch (Exception exc)
{
if (status == ValueTaskSourceStatus.Canceled)
{
#if netstandard
vtst.TrySetCanceled();
#else
if (exc is OperationCanceledException oce)
{
vtst.TrySetCanceled(oce.CancellationToken, oce);
}
else
{
vtst.TrySetCanceled(new CancellationToken(true));
}
#endif
}
else
{
vtst.TrySetException(exc);
}
}
};
/// <summary>The associated <see cref="IValueTaskSource"/>.</summary>
private IValueTaskSource _source;
/// <summary>The token to pass through to operations on <see cref="_source"/></summary>
private readonly short _token;
public ValueTaskSourceAsTask(IValueTaskSource source, short token)
{
_token = token;
_source = source;
source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None);
}
}
/// <summary>Gets whether the <see cref="ValueTask"/> represents a completed operation.</summary>
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj == null)
{
return true;
}
if (obj is Task t)
{
return t.IsCompleted;
}
return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) != ValueTaskSourceStatus.Pending;
}
}
/// <summary>Gets whether the <see cref="ValueTask"/> represents a successfully completed operation.</summary>
public bool IsCompletedSuccessfully
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj == null)
{
return true;
}
if (obj is Task t)
{
return
#if netstandard
t.Status == TaskStatus.RanToCompletion;
#else
t.IsCompletedSuccessfully;
#endif
}
return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Succeeded;
}
}
/// <summary>Gets whether the <see cref="ValueTask"/> represents a failed operation.</summary>
public bool IsFaulted
{
get
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj == null)
{
return false;
}
if (obj is Task t)
{
return t.IsFaulted;
}
return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Faulted;
}
}
/// <summary>Gets whether the <see cref="ValueTask"/> represents a canceled operation.</summary>
/// <remarks>
/// If the <see cref="ValueTask"/> is backed by a result or by a <see cref="IValueTaskSource"/>,
/// this will always return false. If it's backed by a <see cref="Task"/>, it'll return the
/// value of the task's <see cref="Task.IsCanceled"/> property.
/// </remarks>
public bool IsCanceled
{
get
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj == null)
{
return false;
}
if (obj is Task t)
{
return t.IsCanceled;
}
return Unsafe.As<IValueTaskSource>(obj).GetStatus(_token) == ValueTaskSourceStatus.Canceled;
}
}
/// <summary>Throws the exception that caused the <see cref="ValueTask"/> to fail. If it completed successfully, nothing is thrown.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[StackTraceHidden]
internal void ThrowIfCompletedUnsuccessfully()
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
if (obj != null)
{
if (obj is Task t)
{
#if netstandard
t.GetAwaiter().GetResult();
#else
TaskAwaiter.ValidateEnd(t);
#endif
}
else
{
Unsafe.As<IValueTaskSource>(obj).GetResult(_token);
}
}
}
/// <summary>Gets an awaiter for this <see cref="ValueTask"/>.</summary>
public ValueTaskAwaiter GetAwaiter() => new ValueTaskAwaiter(this);
/// <summary>Configures an awaiter for this <see cref="ValueTask"/>.</summary>
/// <param name="continueOnCapturedContext">
/// true to attempt to marshal the continuation back to the captured context; otherwise, false.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) =>
new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _token, continueOnCapturedContext));
}
/// <summary>Provides a value type that can represent a synchronously available value or a task object.</summary>
/// <typeparam name="TResult">Specifies the type of the result.</typeparam>
/// <remarks>
/// <see cref="ValueTask{TResult}"/>s are meant to be directly awaited. To do more complicated operations with them, a <see cref="Task"/>
/// should be extracted using <see cref="AsTask"/> or <see cref="Preserve"/>. Such operations might include caching an instance to
/// be awaited later, registering multiple continuations with a single operation, awaiting the same task multiple times, and using
/// combinators over multiple operations.
/// </remarks>
[AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))]
[StructLayout(LayoutKind.Auto)]
public readonly struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
{
/// <summary>The task to be used if the operation completed asynchronously or if it completed synchronously but non-successfully.</summary>
internal readonly Task<TResult> _task;
/// <summary>A task canceled using `new CancellationToken(true)`. Lazily created only when first needed.</summary>
private static Task<TResult> s_canceledTask;
/// <summary>null if <see cref="_result"/> has the result, otherwise a <see cref="Task{TResult}"/> or a <see cref="IValueTaskSource{TResult}"/>.</summary>
internal readonly object _obj;
/// <summary>The result to be used if the operation completed successfully synchronously.</summary>
internal readonly TResult _result;
/// <summary>Opaque value passed through to the <see cref="IValueTaskSource{TResult}"/>.</summary>
internal readonly short _token;
/// <summary>true to continue on the captured context; otherwise, false.</summary>
/// <remarks>Stored in the <see cref="ValueTask{TResult}"/> rather than in the configured awaiter to utilize otherwise padding space.</remarks>
internal readonly bool _continueOnCapturedContext;
/// <summary>Initialize the <see cref="ValueTask{TResult}"/> with the result of the successful operation.</summary>
// An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation
// with a result of default(TResult).
/// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <typeparamref name="TResult"/> result value.</summary>
/// <param name="result">The result.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(TResult result)
{
_task = null;
_result = result;
_obj = null;
_continueOnCapturedContext = true;
_token = 0;
}
/// <summary>
/// Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="Task{TResult}"/> that represents the operation.
/// </summary>
/// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="Task{TResult}"/> that represents the operation.</summary>
/// <param name="task">The task.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(Task<TResult> task)
{
if (task == null)
@ -76,103 +458,396 @@ namespace System.Threading.Tasks
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task);
}
_task = task;
_result = default(TResult);
_obj = task;
_result = default;
_continueOnCapturedContext = true;
_token = 0;
}
/// <summary>Initialize the <see cref="ValueTask{TResult}"/> with a <see cref="IValueTaskSource{TResult}"/> object that represents the operation.</summary>
/// <param name="source">The source.</param>
/// <param name="token">Opaque value passed through to the <see cref="IValueTaskSource"/>.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTask(IValueTaskSource<TResult> source, short token)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
_obj = source;
_token = token;
_result = default;
_continueOnCapturedContext = true;
}
/// <summary>Non-verified initialization of the struct to the specified values.</summary>
/// <param name="obj">The object.</param>
/// <param name="result">The result.</param>
/// <param name="token">The token.</param>
/// <param name="continueOnCapturedContext">true to continue on captured context; otherwise, false.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ValueTask(object obj, TResult result, short token, bool continueOnCapturedContext)
{
_obj = obj;
_result = result;
_token = token;
_continueOnCapturedContext = continueOnCapturedContext;
}
/// <summary>Returns the hash code for this instance.</summary>
public override int GetHashCode() =>
_task != null ? _task.GetHashCode() :
_obj != null ? _obj.GetHashCode() :
_result != null ? _result.GetHashCode() :
0;
/// <summary>Returns a value indicating whether this value is equal to a specified <see cref="object"/>.</summary>
public override bool Equals(object obj) =>
obj is ValueTask<TResult> &&
obj is ValueTask<TResult> &&
Equals((ValueTask<TResult>)obj);
/// <summary>Returns a value indicating whether this value is equal to a specified <see cref="ValueTask{TResult}"/> value.</summary>
public bool Equals(ValueTask<TResult> other) =>
_task != null || other._task != null ?
_task == other._task :
_obj != null || other._obj != null ?
_obj == other._obj && _token == other._token :
EqualityComparer<TResult>.Default.Equals(_result, other._result);
/// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are equal.</summary>
public static bool operator==(ValueTask<TResult> left, ValueTask<TResult> right) =>
public static bool operator ==(ValueTask<TResult> left, ValueTask<TResult> right) =>
left.Equals(right);
/// <summary>Returns a value indicating whether two <see cref="ValueTask{TResult}"/> values are not equal.</summary>
public static bool operator!=(ValueTask<TResult> left, ValueTask<TResult> right) =>
public static bool operator !=(ValueTask<TResult> left, ValueTask<TResult> right) =>
!left.Equals(right);
/// <summary>
/// Gets a <see cref="Task{TResult}"/> object to represent this ValueTask. It will
/// either return the wrapped task object if one exists, or it'll manufacture a new
/// task object to represent the result.
/// Gets a <see cref="Task{TResult}"/> object to represent this ValueTask.
/// </summary>
public Task<TResult> AsTask() =>
// Return the task if we were constructed from one, otherwise manufacture one. We don't
// cache the generated task into _task as it would end up changing both equality comparison
// and the hash code we generate in GetHashCode.
_task ?? AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result);
/// <remarks>
/// It will either return the wrapped task object if one exists, or it'll
/// manufacture a new task object to represent the result.
/// </remarks>
public Task<TResult> AsTask()
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
internal Task<TResult> AsTaskExpectNonNull() =>
// Return the task if we were constructed from one, otherwise manufacture one.
// Unlike AsTask(), this method is called only when we expect _task to be non-null,
// and thus we don't want GetTaskForResult inlined.
_task ?? GetTaskForResultNoInlining();
if (obj == null)
{
return
#if netstandard
Task.FromResult(_result);
#else
AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result);
#endif
}
[MethodImpl(MethodImplOptions.NoInlining)]
private Task<TResult> GetTaskForResultNoInlining() => AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result);
if (obj is Task<TResult> t)
{
return t;
}
return GetTaskForValueTaskSource(Unsafe.As<IValueTaskSource<TResult>>(obj));
}
/// <summary>Gets a <see cref="ValueTask{TResult}"/> that may be used at any point in the future.</summary>
public ValueTask<TResult> Preserve() => _obj == null ? this : new ValueTask<TResult>(AsTask());
/// <summary>Creates a <see cref="Task{TResult}"/> to represent the <see cref="IValueTaskSource{TResult}"/>.</summary>
/// <remarks>
/// The <see cref="IValueTaskSource{TResult}"/> is passed in rather than reading and casting <see cref="_obj"/>
/// so that the caller can pass in an object it's already validated.
/// </remarks>
private Task<TResult> GetTaskForValueTaskSource(IValueTaskSource<TResult> t)
{
ValueTaskSourceStatus status = t.GetStatus(_token);
if (status != ValueTaskSourceStatus.Pending)
{
try
{
// Get the result of the operation and return a task for it.
// If any exception occurred, propagate it
return
#if netstandard
Task.FromResult(t.GetResult(_token));
#else
AsyncTaskMethodBuilder<TResult>.GetTaskForResult(t.GetResult(_token));
#endif
// If status is Faulted or Canceled, GetResult should throw. But
// we can't guarantee every implementation will do the "right thing".
// If it doesn't throw, we just treat that as success and ignore
// the status.
}
catch (Exception exc)
{
if (status == ValueTaskSourceStatus.Canceled)
{
#if !netstandard
if (exc is OperationCanceledException oce)
{
var task = new Task<TResult>();
task.TrySetCanceled(oce.CancellationToken, oce);
return task;
}
#endif
Task<TResult> canceledTask = s_canceledTask;
if (canceledTask == null)
{
#if netstandard
var tcs = new TaskCompletionSource<TResult>();
tcs.TrySetCanceled();
canceledTask = tcs.Task;
#else
canceledTask = Task.FromCanceled<TResult>(new CancellationToken(true));
#endif
// Benign race condition to initialize cached task, as identity doesn't matter.
s_canceledTask = canceledTask;
}
return canceledTask;
}
else
{
#if netstandard
var tcs = new TaskCompletionSource<TResult>();
tcs.TrySetException(exc);
return tcs.Task;
#else
return Task.FromException<TResult>(exc);
#endif
}
}
}
var m = new ValueTaskSourceAsTask(t, _token);
return
#if netstandard
m.Task;
#else
m;
#endif
}
/// <summary>Type used to create a <see cref="Task{TResult}"/> to represent a <see cref="IValueTaskSource{TResult}"/>.</summary>
private sealed class ValueTaskSourceAsTask :
#if netstandard
TaskCompletionSource<TResult>
#else
Task<TResult>
#endif
{
private static readonly Action<object> s_completionAction = state =>
{
if (!(state is ValueTaskSourceAsTask vtst) ||
!(vtst._source is IValueTaskSource<TResult> source))
{
// This could only happen if the IValueTaskSource<TResult> passed the wrong state
// or if this callback were invoked multiple times such that the state
// was previously nulled out.
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.state);
return;
}
vtst._source = null;
ValueTaskSourceStatus status = source.GetStatus(vtst._token);
try
{
vtst.TrySetResult(source.GetResult(vtst._token));
}
catch (Exception exc)
{
if (status == ValueTaskSourceStatus.Canceled)
{
#if netstandard
vtst.TrySetCanceled();
#else
if (exc is OperationCanceledException oce)
{
vtst.TrySetCanceled(oce.CancellationToken, oce);
}
else
{
vtst.TrySetCanceled(new CancellationToken(true));
}
#endif
}
else
{
vtst.TrySetException(exc);
}
}
};
/// <summary>The associated <see cref="IValueTaskSource"/>.</summary>
private IValueTaskSource<TResult> _source;
/// <summary>The token to pass through to operations on <see cref="_source"/></summary>
private readonly short _token;
public ValueTaskSourceAsTask(IValueTaskSource<TResult> source, short token)
{
_source = source;
_token = token;
source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None);
}
}
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a completed operation.</summary>
public bool IsCompleted => _task == null || _task.IsCompleted;
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
if (obj == null)
{
return true;
}
if (obj is Task<TResult> t)
{
return t.IsCompleted;
}
return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) != ValueTaskSourceStatus.Pending;
}
}
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a successfully completed operation.</summary>
public bool IsCompletedSuccessfully => _task == null || _task.IsCompletedSuccessfully;
public bool IsCompletedSuccessfully
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
if (obj == null)
{
return true;
}
if (obj is Task<TResult> t)
{
return
#if netstandard
t.Status == TaskStatus.RanToCompletion;
#else
t.IsCompletedSuccessfully;
#endif
}
return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Succeeded;
}
}
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a failed operation.</summary>
public bool IsFaulted => _task != null && _task.IsFaulted;
public bool IsFaulted
{
get
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
if (obj == null)
{
return false;
}
if (obj is Task<TResult> t)
{
return t.IsFaulted;
}
return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Faulted;
}
}
/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> represents a canceled operation.</summary>
public bool IsCanceled => _task != null && _task.IsCanceled;
/// <remarks>
/// If the <see cref="ValueTask{TResult}"/> is backed by a result or by a <see cref="IValueTaskSource{TResult}"/>,
/// this will always return false. If it's backed by a <see cref="Task"/>, it'll return the
/// value of the task's <see cref="Task.IsCanceled"/> property.
/// </remarks>
public bool IsCanceled
{
get
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
if (obj == null)
{
return false;
}
if (obj is Task<TResult> t)
{
return t.IsCanceled;
}
return Unsafe.As<IValueTaskSource<TResult>>(obj).GetStatus(_token) == ValueTaskSourceStatus.Canceled;
}
}
/// <summary>Gets the result.</summary>
public TResult Result => _task == null ? _result : _task.GetAwaiter().GetResult();
public TResult Result
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
object obj = _obj;
Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
/// <summary>Gets an awaiter for this value.</summary>
if (obj == null)
{
return _result;
}
if (obj is Task<TResult> t)
{
#if netstandard
return t.GetAwaiter().GetResult();
#else
TaskAwaiter.ValidateEnd(t);
return t.ResultOnSuccess;
#endif
}
return Unsafe.As<IValueTaskSource<TResult>>(obj).GetResult(_token);
}
}
/// <summary>Gets an awaiter for this <see cref="ValueTask{TResult}"/>.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTaskAwaiter<TResult> GetAwaiter() => new ValueTaskAwaiter<TResult>(this);
/// <summary>Configures an awaiter for this value.</summary>
/// <summary>Configures an awaiter for this <see cref="ValueTask{TResult}"/>.</summary>
/// <param name="continueOnCapturedContext">
/// true to attempt to marshal the continuation back to the captured context; otherwise, false.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext) =>
new ConfiguredValueTaskAwaitable<TResult>(this, continueOnCapturedContext);
new ConfiguredValueTaskAwaitable<TResult>(new ValueTask<TResult>(_obj, _result, _token, continueOnCapturedContext));
/// <summary>Gets a string-representation of this <see cref="ValueTask{TResult}"/>.</summary>
public override string ToString()
{
if (_task != null)
if (IsCompletedSuccessfully)
{
return _task.IsCompletedSuccessfully && _task.Result != null ?
_task.Result.ToString() :
string.Empty;
}
else
{
return _result != null ?
_result.ToString() :
string.Empty;
TResult result = Result;
if (result != null)
{
return result.ToString();
}
}
return string.Empty;
}
// TODO https://github.com/dotnet/corefx/issues/22171:
// Remove CreateAsyncMethodBuilder once the C# compiler relies on the AsyncBuilder attribute.
/// <summary>Creates a method builder for use with an async method.</summary>
/// <returns>The created builder.</returns>
[EditorBrowsable(EditorBrowsableState.Never)] // intended only for compiler consumption
public static AsyncValueTaskMethodBuilder<TResult> CreateAsyncMethodBuilder() => AsyncValueTaskMethodBuilder<TResult>.Create();
}
}
}