Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs')
-rw-r--r--src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs510
1 files changed, 310 insertions, 200 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs b/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs
index 18e4a27a4..4d6a75960 100644
--- a/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs
+++ b/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs
@@ -14,17 +14,59 @@ using Internal.Runtime.CompilerServices;
namespace System.Threading.Tasks
{
+ // 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>
- /// <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.
+ /// <see cref="ValueTask"/> instances 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 a task instance to be awaited later,
+ /// registering multiple continuations with a single task, awaiting the same task multiple times, and using combinators over
+ /// multiple operations:
+ /// <list type="bullet">
+ /// <item>
+ /// Once the result of a <see cref="ValueTask"/> instance has been retrieved, do not attempt to retrieve it again.
+ /// <see cref="ValueTask"/> instances may be backed by <see cref="IValueTaskSource"/> instances that are reusable, and such
+ /// instances may use the act of retrieving the instances result as a notification that the instance may now be reused for
+ /// a different operation. Attempting to then reuse that same <see cref="ValueTask"/> results in undefined behavior.
+ /// </item>
+ /// <item>
+ /// Do not attempt to add multiple continuations to the same <see cref="ValueTask"/>. While this might work if the
+ /// <see cref="ValueTask"/> wraps a <code>T</code> or a <see cref="Task"/>, it may not work if the <see cref="ValueTask"/>
+ /// was constructed from an <see cref="IValueTaskSource"/>.
+ /// </item>
+ /// <item>
+ /// Some operations that return a <see cref="ValueTask"/> may invalidate it based on some subsequent operation being performed.
+ /// Unless otherwise documented, assume that a <see cref="ValueTask"/> should be awaited prior to performing any additional operations
+ /// on the instance from which it was retrieved.
+ /// </item>
+ /// </list>
/// </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);
@@ -34,10 +76,11 @@ namespace System.Threading.Tasks
/// <summary>null if representing a successful synchronous completion, otherwise a <see cref="Task"/> or a <see cref="IValueTaskSource"/>.</summary>
internal readonly object _obj;
- /// <summary>Flags providing additional details about the ValueTask's contents and behavior.</summary>
- internal readonly ValueTaskFlags _flags;
/// <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.
@@ -53,7 +96,7 @@ namespace System.Threading.Tasks
_obj = task;
- _flags = ValueTaskFlags.ObjectIsTask;
+ _continueOnCapturedContext = true;
_token = 0;
}
@@ -71,51 +114,15 @@ namespace System.Threading.Tasks
_obj = source;
_token = token;
- _flags = 0;
+ _continueOnCapturedContext = true;
}
- /// <summary>Non-verified initialization of the struct to the specified values.</summary>
- /// <param name="obj">The object.</param>
- /// <param name="token">The token.</param>
- /// <param name="flags">The flags.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private ValueTask(object obj, short token, ValueTaskFlags flags)
+ private ValueTask(object obj, short token, bool continueOnCapturedContext)
{
_obj = obj;
_token = token;
- _flags = flags;
- }
-
- /// <summary>Gets whether the contination should be scheduled to the current context.</summary>
- internal bool ContinueOnCapturedContext
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_flags & ValueTaskFlags.AvoidCapturedContext) == 0;
- }
-
- /// <summary>Gets whether the object in the <see cref="_obj"/> field is a <see cref="Task"/>.</summary>
- internal bool ObjectIsTask
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_flags & ValueTaskFlags.ObjectIsTask) != 0;
- }
-
- /// <summary>Returns the <see cref="Task"/> stored in <see cref="_obj"/>. This uses <see cref="Unsafe"/>.</summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal Task UnsafeGetTask()
- {
- Debug.Assert(ObjectIsTask);
- Debug.Assert(_obj is Task);
- return Unsafe.As<Task>(_obj);
- }
-
- /// <summary>Returns the <see cref="IValueTaskSource"/> stored in <see cref="_obj"/>. This uses <see cref="Unsafe"/>.</summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal IValueTaskSource UnsafeGetValueTaskSource()
- {
- Debug.Assert(!ObjectIsTask);
- Debug.Assert(_obj is IValueTaskSource);
- return Unsafe.As<IValueTaskSource>(_obj);
+ _continueOnCapturedContext = continueOnCapturedContext;
}
/// <summary>Returns the hash code for this instance.</summary>
@@ -144,18 +151,26 @@ namespace System.Threading.Tasks
/// 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() =>
- _obj == null ? ValueTask.CompletedTask :
- ObjectIsTask ? UnsafeGetTask() :
- GetTaskForValueTaskSource();
+ 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>
- private Task GetTaskForValueTaskSource()
+ /// <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)
{
- IValueTaskSource t = UnsafeGetValueTaskSource();
ValueTaskSourceStatus status = t.GetStatus(_token);
if (status != ValueTaskSourceStatus.Pending)
{
@@ -164,7 +179,7 @@ namespace System.Threading.Tasks
// Propagate any exceptions that may have occurred, then return
// an already successfully completed task.
t.GetResult(_token);
- return ValueTask.CompletedTask;
+ return CompletedTask;
// If status is Faulted or Canceled, GetResult should throw. But
// we can't guarantee every implementation will do the "right thing".
@@ -175,22 +190,15 @@ namespace System.Threading.Tasks
{
if (status == ValueTaskSourceStatus.Canceled)
{
-#if netstandard
- var tcs = new TaskCompletionSource<bool>();
- tcs.TrySetCanceled();
- return tcs.Task;
-#else
+#if !netstandard
if (exc is OperationCanceledException oce)
{
var task = new Task<VoidTaskResult>();
task.TrySetCanceled(oce.CancellationToken, oce);
return task;
}
- else
- {
- return Task.FromCanceled(new CancellationToken(true));
- }
#endif
+ return s_canceledTask;
}
else
{
@@ -205,7 +213,7 @@ namespace System.Threading.Tasks
}
}
- var m = new ValueTaskSourceTask(t, _token);
+ var m = new ValueTaskSourceAsTask(t, _token);
return
#if netstandard
m.Task;
@@ -215,7 +223,7 @@ namespace System.Threading.Tasks
}
/// <summary>Type used to create a <see cref="Task"/> to represent a <see cref="IValueTaskSource"/>.</summary>
- private sealed class ValueTaskSourceTask :
+ private sealed class ValueTaskSourceAsTask :
#if netstandard
TaskCompletionSource<bool>
#else
@@ -224,7 +232,7 @@ namespace System.Threading.Tasks
{
private static readonly Action<object> s_completionAction = state =>
{
- if (!(state is ValueTaskSourceTask vtst) ||
+ if (!(state is ValueTaskSourceAsTask vtst) ||
!(vtst._source is IValueTaskSource source))
{
// This could only happen if the IValueTaskSource passed the wrong state
@@ -270,7 +278,7 @@ namespace System.Threading.Tasks
/// <summary>The token to pass through to operations on <see cref="_source"/></summary>
private readonly short _token;
- public ValueTaskSourceTask(IValueTaskSource source, short token)
+ public ValueTaskSourceAsTask(IValueTaskSource source, short token)
{
_token = token;
_source = source;
@@ -282,30 +290,73 @@ namespace System.Threading.Tasks
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _obj == null || (ObjectIsTask ? UnsafeGetTask().IsCompleted : UnsafeGetValueTaskSource().GetStatus(_token) != ValueTaskSourceStatus.Pending);
+ 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 =>
- _obj == null ||
- (ObjectIsTask ?
+ 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
- UnsafeTask.Status == TaskStatus.RanToCompletion :
+ t.Status == TaskStatus.RanToCompletion;
#else
- UnsafeGetTask().IsCompletedSuccessfully :
+ t.IsCompletedSuccessfully;
#endif
- UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Succeeded);
+ }
+
+ 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 =>
- _obj != null &&
- (ObjectIsTask ? UnsafeGetTask().IsFaulted : UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Faulted);
+ 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>
@@ -316,9 +367,23 @@ namespace System.Threading.Tasks
/// </remarks>
public bool IsCanceled
{
- get =>
- _obj != null &&
- (ObjectIsTask ? UnsafeGetTask().IsCanceled : UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Canceled);
+ 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>
@@ -326,19 +391,22 @@ namespace System.Threading.Tasks
[StackTraceHidden]
internal void ThrowIfCompletedUnsuccessfully()
{
- if (_obj != null)
+ object obj = _obj;
+ Debug.Assert(obj == null || obj is Task || obj is IValueTaskSource);
+
+ if (obj != null)
{
- if (ObjectIsTask)
+ if (obj is Task t)
{
#if netstandard
- UnsafeTask.GetAwaiter().GetResult();
+ t.GetAwaiter().GetResult();
#else
- TaskAwaiter.ValidateEnd(UnsafeGetTask());
+ TaskAwaiter.ValidateEnd(t);
#endif
}
else
{
- UnsafeGetValueTaskSource().GetResult(_token);
+ Unsafe.As<IValueTaskSource>(obj).GetResult(_token);
}
}
}
@@ -351,34 +419,51 @@ namespace System.Threading.Tasks
/// true to attempt to marshal the continuation back to the captured context; otherwise, false.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)
- {
- // TODO: Simplify once https://github.com/dotnet/coreclr/pull/16138 is fixed.
- bool avoidCapture = !continueOnCapturedContext;
- return new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _token, _flags | Unsafe.As<bool, ValueTaskFlags>(ref avoidCapture)));
- }
+ 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.
+ /// <see cref="ValueTask{TResult}"/> instances are meant to be directly awaited. To do more complicated operations with them, a <see cref="Task{TResult}"/>
+ /// should be extracted using <see cref="AsTask"/>. Such operations might include caching a task instance to be awaited later,
+ /// registering multiple continuations with a single task, awaiting the same task multiple times, and using combinators over
+ /// multiple operations:
+ /// <list type="bullet">
+ /// <item>
+ /// Once the result of a <see cref="ValueTask{TResult}"/> instance has been retrieved, do not attempt to retrieve it again.
+ /// <see cref="ValueTask{TResult}"/> instances may be backed by <see cref="IValueTaskSource{TResult}"/> instances that are reusable, and such
+ /// instances may use the act of retrieving the instances result as a notification that the instance may now be reused for
+ /// a different operation. Attempting to then reuse that same <see cref="ValueTask{TResult}"/> results in undefined behavior.
+ /// </item>
+ /// <item>
+ /// Do not attempt to add multiple continuations to the same <see cref="ValueTask{TResult}"/>. While this might work if the
+ /// <see cref="ValueTask{TResult}"/> wraps a <code>T</code> or a <see cref="Task{TResult}"/>, it may not work if the <see cref="Task{TResult}"/>
+ /// was constructed from an <see cref="IValueTaskSource{TResult}"/>.
+ /// </item>
+ /// <item>
+ /// Some operations that return a <see cref="ValueTask{TResult}"/> may invalidate it based on some subsequent operation being performed.
+ /// Unless otherwise documented, assume that a <see cref="ValueTask{TResult}"/> should be awaited prior to performing any additional operations
+ /// on the instance from which it was retrieved.
+ /// </item>
+ /// </list>
/// </remarks>
[AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder<>))]
[StructLayout(LayoutKind.Auto)]
public readonly struct ValueTask<TResult> : IEquatable<ValueTask<TResult>>
{
+ /// <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>Flags providing additional details about the ValueTask's contents and behavior.</summary>
- internal readonly ValueTaskFlags _flags;
/// <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;
// An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation
// with a result of default(TResult).
@@ -391,7 +476,7 @@ namespace System.Threading.Tasks
_result = result;
_obj = null;
- _flags = 0;
+ _continueOnCapturedContext = true;
_token = 0;
}
@@ -408,7 +493,7 @@ namespace System.Threading.Tasks
_obj = task;
_result = default;
- _flags = ValueTaskFlags.ObjectIsTask;
+ _continueOnCapturedContext = true;
_token = 0;
}
@@ -427,54 +512,23 @@ namespace System.Threading.Tasks
_token = token;
_result = default;
- _flags = 0;
+ _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="flags">The flags.</param>
+ /// <param name="continueOnCapturedContext">true to continue on captured context; otherwise, false.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private ValueTask(object obj, TResult result, short token, ValueTaskFlags flags)
+ private ValueTask(object obj, TResult result, short token, bool continueOnCapturedContext)
{
_obj = obj;
_result = result;
_token = token;
- _flags = flags;
- }
-
- /// <summary>Gets whether the contination should be scheduled to the current context.</summary>
- internal bool ContinueOnCapturedContext
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_flags & ValueTaskFlags.AvoidCapturedContext) == 0;
- }
-
- /// <summary>Gets whether the object in the <see cref="_obj"/> field is a <see cref="Task{TResult}"/>.</summary>
- internal bool ObjectIsTask
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => (_flags & ValueTaskFlags.ObjectIsTask) != 0;
+ _continueOnCapturedContext = continueOnCapturedContext;
}
- /// <summary>Returns the <see cref="Task{TResult}"/> stored in <see cref="_obj"/>. This uses <see cref="Unsafe"/>.</summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal Task<TResult> UnsafeGetTask()
- {
- Debug.Assert(ObjectIsTask);
- Debug.Assert(_obj is Task<TResult>);
- return Unsafe.As<Task<TResult>>(_obj);
- }
-
- /// <summary>Returns the <see cref="IValueTaskSource{TResult}"/> stored in <see cref="_obj"/>. This uses <see cref="Unsafe"/>.</summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal IValueTaskSource<TResult> UnsafeGetValueTaskSource()
- {
- Debug.Assert(!ObjectIsTask);
- Debug.Assert(_obj is IValueTaskSource<TResult>);
- return Unsafe.As<IValueTaskSource<TResult>>(_obj);
- }
/// <summary>Returns the hash code for this instance.</summary>
public override int GetHashCode() =>
@@ -508,23 +562,39 @@ namespace System.Threading.Tasks
/// 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() =>
- _obj == null ?
+ public Task<TResult> AsTask()
+ {
+ object obj = _obj;
+ Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
+
+ if (obj == null)
+ {
+ return
#if netstandard
- Task.FromResult(_result) :
+ Task.FromResult(_result);
#else
- AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result) :
+ AsyncTaskMethodBuilder<TResult>.GetTaskForResult(_result);
#endif
- ObjectIsTask ? UnsafeGetTask() :
- GetTaskForValueTaskSource();
+ }
+
+ 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>
- private Task<TResult> GetTaskForValueTaskSource()
+ /// <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)
{
- IValueTaskSource<TResult> t = UnsafeGetValueTaskSource();
ValueTaskSourceStatus status = t.GetStatus(_token);
if (status != ValueTaskSourceStatus.Pending)
{
@@ -548,22 +618,29 @@ namespace System.Threading.Tasks
{
if (status == ValueTaskSourceStatus.Canceled)
{
-#if netstandard
- var tcs = new TaskCompletionSource<TResult>();
- tcs.TrySetCanceled();
- return tcs.Task;
-#else
+#if !netstandard
if (exc is OperationCanceledException oce)
{
var task = new Task<TResult>();
task.TrySetCanceled(oce.CancellationToken, oce);
return task;
}
- else
+#endif
+
+ Task<TResult> canceledTask = s_canceledTask;
+ if (canceledTask == null)
{
- return Task.FromCanceled<TResult>(new CancellationToken(true));
- }
+#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
{
@@ -578,7 +655,7 @@ namespace System.Threading.Tasks
}
}
- var m = new ValueTaskSourceTask(t, _token);
+ var m = new ValueTaskSourceAsTask(t, _token);
return
#if netstandard
m.Task;
@@ -588,7 +665,7 @@ namespace System.Threading.Tasks
}
/// <summary>Type used to create a <see cref="Task{TResult}"/> to represent a <see cref="IValueTaskSource{TResult}"/>.</summary>
- private sealed class ValueTaskSourceTask :
+ private sealed class ValueTaskSourceAsTask :
#if netstandard
TaskCompletionSource<TResult>
#else
@@ -597,7 +674,7 @@ namespace System.Threading.Tasks
{
private static readonly Action<object> s_completionAction = state =>
{
- if (!(state is ValueTaskSourceTask vtst) ||
+ if (!(state is ValueTaskSourceAsTask vtst) ||
!(vtst._source is IValueTaskSource<TResult> source))
{
// This could only happen if the IValueTaskSource<TResult> passed the wrong state
@@ -642,7 +719,7 @@ namespace System.Threading.Tasks
/// <summary>The token to pass through to operations on <see cref="_source"/></summary>
private readonly short _token;
- public ValueTaskSourceTask(IValueTaskSource<TResult> source, short token)
+ public ValueTaskSourceAsTask(IValueTaskSource<TResult> source, short token)
{
_source = source;
_token = token;
@@ -654,30 +731,73 @@ namespace System.Threading.Tasks
public bool IsCompleted
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _obj == null || (ObjectIsTask ? UnsafeGetTask().IsCompleted : UnsafeGetValueTaskSource().GetStatus(_token) != ValueTaskSourceStatus.Pending);
+ 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
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get =>
- _obj == null ||
- (ObjectIsTask ?
+ 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
- UnsafeTask.Status == TaskStatus.RanToCompletion :
+ t.Status == TaskStatus.RanToCompletion;
#else
- UnsafeGetTask().IsCompletedSuccessfully :
+ t.IsCompletedSuccessfully;
#endif
- UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Succeeded);
+ }
+
+ 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
{
- get =>
- _obj != null &&
- (ObjectIsTask ? UnsafeGetTask().IsFaulted : UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Faulted);
+ 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>
@@ -688,9 +808,23 @@ namespace System.Threading.Tasks
/// </remarks>
public bool IsCanceled
{
- get =>
- _obj != null &&
- (ObjectIsTask ? UnsafeGetTask().IsCanceled : UnsafeGetValueTaskSource().GetStatus(_token) == ValueTaskSourceStatus.Canceled);
+ 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>
@@ -699,23 +833,25 @@ namespace System.Threading.Tasks
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
- if (_obj == null)
+ object obj = _obj;
+ Debug.Assert(obj == null || obj is Task<TResult> || obj is IValueTaskSource<TResult>);
+
+ if (obj == null)
{
return _result;
}
- if (ObjectIsTask)
+ if (obj is Task<TResult> t)
{
#if netstandard
- return UnsafeTask.GetAwaiter().GetResult();
+ return t.GetAwaiter().GetResult();
#else
- Task<TResult> t = UnsafeGetTask();
TaskAwaiter.ValidateEnd(t);
return t.ResultOnSuccess;
#endif
}
- return UnsafeGetValueTaskSource().GetResult(_token);
+ return Unsafe.As<IValueTaskSource<TResult>>(obj).GetResult(_token);
}
}
@@ -728,12 +864,8 @@ namespace System.Threading.Tasks
/// true to attempt to marshal the continuation back to the captured context; otherwise, false.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext)
- {
- // TODO: Simplify once https://github.com/dotnet/coreclr/pull/16138 is fixed.
- bool avoidCapture = !continueOnCapturedContext;
- return new ConfiguredValueTaskAwaitable<TResult>(new ValueTask<TResult>(_obj, _result, _token, _flags | Unsafe.As<bool, ValueTaskFlags>(ref avoidCapture)));
- }
+ public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool 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()
@@ -750,26 +882,4 @@ namespace System.Threading.Tasks
return string.Empty;
}
}
-
- /// <summary>Internal flags used in the implementation of <see cref="ValueTask"/> and <see cref="ValueTask{TResult}"/>.</summary>
- [Flags]
- internal enum ValueTaskFlags : byte
- {
- /// <summary>
- /// Indicates that context (e.g. SynchronizationContext) should not be captured when adding
- /// a continuation.
- /// </summary>
- /// <remarks>
- /// The value here must be 0x1, to match the value of a true Boolean reinterpreted as a byte.
- /// This only has meaning when awaiting a ValueTask, with ConfigureAwait creating a new
- /// ValueTask setting or not setting this flag appropriately.
- /// </remarks>
- AvoidCapturedContext = 0x1,
-
- /// <summary>
- /// Indicates that the ValueTask's object field stores a Task. This is used to avoid
- /// a type check on whatever is stored in the object field.
- /// </summary>
- ObjectIsTask = 0x2
- }
-} \ No newline at end of file
+}