From 6676f5374d915b02124f8756a8d3e4dc76e72f34 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 28 Feb 2018 22:25:02 -0500 Subject: [PATCH] Add token to IValueTaskSource --- .../ConfiguredValueTaskAwaitable.cs | 12 +- .../CompilerServices/ValueTaskAwaiter.cs | 12 +- .../Threading/Tasks/IValueTaskSource.cs | 20 ++-- .../System/Threading/Tasks/ValueTask.cs | 104 +++++++++++------- 4 files changed, 91 insertions(+), 57 deletions(-) diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs index 295a116a7846..0d7e3d11192c 100644 --- a/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -63,7 +63,7 @@ public void OnCompleted(Action continuation) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, + _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.FlowExecutionContext | (_value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None)); } @@ -82,7 +82,7 @@ public void UnsafeOnCompleted(Action continuation) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, + _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, _value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None); } else @@ -100,7 +100,7 @@ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, + _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token, _value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None); } else @@ -165,7 +165,7 @@ public void OnCompleted(Action continuation) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, + _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.FlowExecutionContext | (_value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None)); } @@ -184,7 +184,7 @@ public void UnsafeOnCompleted(Action continuation) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, + _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, _value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None); } else @@ -202,7 +202,7 @@ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, + _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token, _value.ContinueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None); } else diff --git a/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs index 943b158c6f36..4b3df947adaa 100644 --- a/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs +++ b/src/mscorlib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -53,7 +53,7 @@ public void OnCompleted(Action continuation) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(s_invokeActionDelegate, continuation, ValueTaskSourceOnCompletedFlags.UseSchedulingContext | ValueTaskSourceOnCompletedFlags.FlowExecutionContext); + _value.UnsafeValueTaskSource.OnCompleted(s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext | ValueTaskSourceOnCompletedFlags.FlowExecutionContext); } else { @@ -70,7 +70,7 @@ public void UnsafeOnCompleted(Action continuation) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(s_invokeActionDelegate, continuation, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); + _value.UnsafeValueTaskSource.OnCompleted(s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); } else { @@ -87,7 +87,7 @@ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(s_invokeAsyncStateMachineBox, box, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); + _value.UnsafeValueTaskSource.OnCompleted(s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); } else { @@ -144,7 +144,7 @@ public void OnCompleted(Action continuation) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, ValueTaskSourceOnCompletedFlags.UseSchedulingContext | ValueTaskSourceOnCompletedFlags.FlowExecutionContext); + _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext | ValueTaskSourceOnCompletedFlags.FlowExecutionContext); } else { @@ -161,7 +161,7 @@ public void UnsafeOnCompleted(Action continuation) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); + _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeActionDelegate, continuation, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); } else { @@ -178,7 +178,7 @@ void IValueTaskAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox box) } else if (_value._obj != null) { - _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); + _value.UnsafeValueTaskSource.OnCompleted(ValueTaskAwaiter.s_invokeAsyncStateMachineBox, box, _value._token, ValueTaskSourceOnCompletedFlags.UseSchedulingContext); } else { diff --git a/src/mscorlib/shared/System/Threading/Tasks/IValueTaskSource.cs b/src/mscorlib/shared/System/Threading/Tasks/IValueTaskSource.cs index b44687fe5c44..7c7312ac06a5 100644 --- a/src/mscorlib/shared/System/Threading/Tasks/IValueTaskSource.cs +++ b/src/mscorlib/shared/System/Threading/Tasks/IValueTaskSource.cs @@ -46,16 +46,19 @@ public enum ValueTaskSourceStatus public interface IValueTaskSource { /// Gets the status of the current operation. - ValueTaskSourceStatus Status { get; } - + /// Opaque value that was provided to the 's constructor. + ValueTaskSourceStatus GetStatus(short token); + /// Schedules the continuation action for this . /// The continuation to invoke when the operation has completed. /// The state object to pass to when it's invoked. + /// Opaque value that was provided to the 's constructor. /// The flags describing the behavior of the continuation. - void OnCompleted(Action continuation, object state, ValueTaskSourceOnCompletedFlags flags); + void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags); /// Gets the result of the . - void GetResult(); + /// Opaque value that was provided to the 's constructor. + void GetResult(short token); } /// Represents an object that can be wrapped by a . @@ -63,15 +66,18 @@ public interface IValueTaskSource public interface IValueTaskSource { /// Gets the status of the current operation. - ValueTaskSourceStatus Status { get; } + /// Opaque value that was provided to the 's constructor. + ValueTaskSourceStatus GetStatus(short token); /// Schedules the continuation action for this . /// The continuation to invoke when the operation has completed. /// The state object to pass to when it's invoked. + /// Opaque value that was provided to the 's constructor. /// The flags describing the behavior of the continuation. - void OnCompleted(Action continuation, object state, ValueTaskSourceOnCompletedFlags flags); + void OnCompleted(Action continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags); /// Gets the result of the . - TResult GetResult(); + /// Opaque value that was provided to the 's constructor. + TResult GetResult(short token); } } diff --git a/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs b/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs index cb7c613529ff..b6689c1d82fe 100644 --- a/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs +++ b/src/mscorlib/shared/System/Threading/Tasks/ValueTask.cs @@ -12,12 +12,12 @@ namespace System.Threading.Tasks { - /// Provides a value type that can represent a task object or a synchronously completed success result. + /// Provides an awaitable result of an asynchronous operation. /// /// s are meant to be directly awaited. To do more complicated operations with them, a - /// should be extracted using or . 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. + /// should be extracted using . 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. /// [AsyncMethodBuilder(typeof(AsyncValueTaskMethodBuilder))] [StructLayout(LayoutKind.Auto)] @@ -32,6 +32,8 @@ namespace System.Threading.Tasks internal readonly object _obj; /// Flags providing additional details about the ValueTask's contents and behavior. internal readonly ValueTaskFlags _flags; + /// Opaque value passed through to the . + internal readonly short _token; // An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation. @@ -46,13 +48,16 @@ public ValueTask(Task task) } _obj = task; + _flags = ValueTaskFlags.ObjectIsTask; + _token = 0; } /// Initialize the with a object that represents the operation. /// The source. + /// Opaque value passed through to the . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask(IValueTaskSource source) + public ValueTask(IValueTaskSource source, short token) { if (source == null) { @@ -60,16 +65,20 @@ public ValueTask(IValueTaskSource source) } _obj = source; + _token = token; + _flags = 0; } /// Non-verified initialization of the struct to the specified values. /// The object. + /// The token. /// The flags. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ValueTask(object obj, ValueTaskFlags flags) + private ValueTask(object obj, short token, ValueTaskFlags flags) { _obj = obj; + _token = token; _flags = flags; } @@ -120,7 +129,7 @@ obj is ValueTask && Equals((ValueTask)obj); /// Returns a value indicating whether this value is equal to a specified value. - public bool Equals(ValueTask other) => _obj == other._obj; + public bool Equals(ValueTask other) => _obj == other._obj && _token == other._token; /// Returns a value indicating whether two values are equal. public static bool operator ==(ValueTask left, ValueTask right) => @@ -154,14 +163,14 @@ public Task AsTask() => private Task GetTaskForValueTaskSource() { IValueTaskSource t = UnsafeValueTaskSource; - ValueTaskSourceStatus status = t.Status; + 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(); + t.GetResult(_token); return #if netstandard s_completedTask; @@ -208,7 +217,7 @@ private Task GetTaskForValueTaskSource() } } - var m = new ValueTaskSourceTask(t); + var m = new ValueTaskSourceTask(t, _token); return #if netstandard m.Task; @@ -238,10 +247,10 @@ private sealed class ValueTaskSourceTask : } vtst._source = null; - ValueTaskSourceStatus status = source.Status; + ValueTaskSourceStatus status = source.GetStatus(vtst._token); try { - source.GetResult(); + source.GetResult(vtst._token); vtst.TrySetResult(default); } catch (Exception exc) @@ -268,12 +277,16 @@ private sealed class ValueTaskSourceTask : } }; + /// The associated . private IValueTaskSource _source; + /// The token to pass through to operations on + private readonly short _token; - public ValueTaskSourceTask(IValueTaskSource source) + public ValueTaskSourceTask(IValueTaskSource source, short token) { + _token = token; _source = source; - source.OnCompleted(s_completionAction, this, ValueTaskSourceOnCompletedFlags.None); + source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None); } } @@ -281,7 +294,7 @@ public ValueTaskSourceTask(IValueTaskSource source) public bool IsCompleted { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _obj == null || (ObjectIsTask ? UnsafeTask.IsCompleted : UnsafeValueTaskSource.Status != ValueTaskSourceStatus.Pending); + get => _obj == null || (ObjectIsTask ? UnsafeTask.IsCompleted : UnsafeValueTaskSource.GetStatus(_token) != ValueTaskSourceStatus.Pending); } /// Gets whether the represents a successfully completed operation. @@ -296,7 +309,7 @@ public bool IsCompletedSuccessfully #else UnsafeTask.IsCompletedSuccessfully : #endif - UnsafeValueTaskSource.Status == ValueTaskSourceStatus.Succeeded); + UnsafeValueTaskSource.GetStatus(_token) == ValueTaskSourceStatus.Succeeded); } /// Gets whether the represents a failed operation. @@ -304,7 +317,7 @@ public bool IsFaulted { get => _obj != null && - (ObjectIsTask ? UnsafeTask.IsFaulted : UnsafeValueTaskSource.Status == ValueTaskSourceStatus.Faulted); + (ObjectIsTask ? UnsafeTask.IsFaulted : UnsafeValueTaskSource.GetStatus(_token) == ValueTaskSourceStatus.Faulted); } /// Gets whether the represents a canceled operation. @@ -317,7 +330,7 @@ public bool IsCanceled { get => _obj != null && - (ObjectIsTask ? UnsafeTask.IsCanceled : UnsafeValueTaskSource.Status == ValueTaskSourceStatus.Canceled); + (ObjectIsTask ? UnsafeTask.IsCanceled : UnsafeValueTaskSource.GetStatus(_token) == ValueTaskSourceStatus.Canceled); } /// Throws the exception that caused the to fail. If it completed successfully, nothing is thrown. @@ -337,7 +350,7 @@ internal void ThrowIfCompletedUnsuccessfully() } else { - UnsafeValueTaskSource.GetResult(); + UnsafeValueTaskSource.GetResult(_token); } } } @@ -354,7 +367,7 @@ public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContex { // TODO: Simplify once https://github.com/dotnet/coreclr/pull/16138 is fixed. bool avoidCapture = !continueOnCapturedContext; - return new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _flags | Unsafe.As(ref avoidCapture))); + return new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _token, _flags | Unsafe.As(ref avoidCapture))); } } @@ -376,6 +389,8 @@ public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContex internal readonly TResult _result; /// Flags providing additional details about the ValueTask's contents and behavior. internal readonly ValueTaskFlags _flags; + /// Opaque value passed through to the . + internal readonly short _token; // An instance created with the default ctor (a zero init'd struct) represents a synchronously, successfully completed operation // with a result of default(TResult). @@ -385,9 +400,11 @@ public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContex [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask(TResult result) { - _obj = null; _result = result; + + _obj = null; _flags = 0; + _token = 0; } /// Initialize the with a that represents the operation. @@ -401,14 +418,17 @@ public ValueTask(Task task) } _obj = task; + _result = default; _flags = ValueTaskFlags.ObjectIsTask; + _token = 0; } /// Initialize the with a object that represents the operation. /// The source. + /// Opaque value passed through to the . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask(IValueTaskSource source) + public ValueTask(IValueTaskSource source, short token) { if (source == null) { @@ -416,6 +436,8 @@ public ValueTask(IValueTaskSource source) } _obj = source; + _token = token; + _result = default; _flags = 0; } @@ -423,12 +445,14 @@ public ValueTask(IValueTaskSource source) /// Non-verified initialization of the struct to the specified values. /// The object. /// The result. + /// The token. /// The flags. [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ValueTask(object obj, TResult result, ValueTaskFlags flags) + private ValueTask(object obj, TResult result, short token, ValueTaskFlags flags) { _obj = obj; _result = result; + _token = token; _flags = flags; } @@ -484,7 +508,7 @@ obj is ValueTask && /// Returns a value indicating whether this value is equal to a specified value. public bool Equals(ValueTask other) => _obj != null || other._obj != null ? - _obj == other._obj : + _obj == other._obj && _token == other._token : EqualityComparer.Default.Equals(_result, other._result); /// Returns a value indicating whether two values are equal. @@ -519,7 +543,7 @@ public Task AsTask() => private Task GetTaskForValueTaskSource() { IValueTaskSource t = UnsafeValueTaskSource; - ValueTaskSourceStatus status = t.Status; + ValueTaskSourceStatus status = t.GetStatus(_token); if (status != ValueTaskSourceStatus.Pending) { try @@ -528,9 +552,9 @@ private Task GetTaskForValueTaskSource() // If any exception occurred, propagate it return #if netstandard - Task.FromResult(t.GetResult()); + Task.FromResult(t.GetResult(_token)); #else - AsyncTaskMethodBuilder.GetTaskForResult(t.GetResult()); + AsyncTaskMethodBuilder.GetTaskForResult(t.GetResult(_token)); #endif // If status is Faulted or Canceled, GetResult should throw. But @@ -572,7 +596,7 @@ private Task GetTaskForValueTaskSource() } } - var m = new ValueTaskSourceTask(t); + var m = new ValueTaskSourceTask(t, _token); return #if netstandard m.Task; @@ -602,10 +626,10 @@ private sealed class ValueTaskSourceTask : } vtst._source = null; - ValueTaskSourceStatus status = source.Status; + ValueTaskSourceStatus status = source.GetStatus(vtst._token); try { - vtst.TrySetResult(source.GetResult()); + vtst.TrySetResult(source.GetResult(vtst._token)); } catch (Exception exc) { @@ -631,12 +655,16 @@ private sealed class ValueTaskSourceTask : } }; + /// The associated . private IValueTaskSource _source; + /// The token to pass through to operations on + private readonly short _token; - public ValueTaskSourceTask(IValueTaskSource source) + public ValueTaskSourceTask(IValueTaskSource source, short token) { _source = source; - source.OnCompleted(s_completionAction, this, ValueTaskSourceOnCompletedFlags.None); + _token = token; + source.OnCompleted(s_completionAction, this, token, ValueTaskSourceOnCompletedFlags.None); } } @@ -644,7 +672,7 @@ public ValueTaskSourceTask(IValueTaskSource source) public bool IsCompleted { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _obj == null || (ObjectIsTask ? UnsafeTask.IsCompleted : UnsafeValueTaskSource.Status != ValueTaskSourceStatus.Pending); + get => _obj == null || (ObjectIsTask ? UnsafeTask.IsCompleted : UnsafeValueTaskSource.GetStatus(_token) != ValueTaskSourceStatus.Pending); } /// Gets whether the represents a successfully completed operation. @@ -659,7 +687,7 @@ public bool IsCompletedSuccessfully #else UnsafeTask.IsCompletedSuccessfully : #endif - UnsafeValueTaskSource.Status == ValueTaskSourceStatus.Succeeded); + UnsafeValueTaskSource.GetStatus(_token) == ValueTaskSourceStatus.Succeeded); } /// Gets whether the represents a failed operation. @@ -667,7 +695,7 @@ public bool IsFaulted { get => _obj != null && - (ObjectIsTask ? UnsafeTask.IsFaulted : UnsafeValueTaskSource.Status == ValueTaskSourceStatus.Faulted); + (ObjectIsTask ? UnsafeTask.IsFaulted : UnsafeValueTaskSource.GetStatus(_token) == ValueTaskSourceStatus.Faulted); } /// Gets whether the represents a canceled operation. @@ -680,7 +708,7 @@ public bool IsCanceled { get => _obj != null && - (ObjectIsTask ? UnsafeTask.IsCanceled : UnsafeValueTaskSource.Status == ValueTaskSourceStatus.Canceled); + (ObjectIsTask ? UnsafeTask.IsCanceled : UnsafeValueTaskSource.GetStatus(_token) == ValueTaskSourceStatus.Canceled); } /// Gets the result. @@ -705,7 +733,7 @@ public TResult Result #endif } - return UnsafeValueTaskSource.GetResult(); + return UnsafeValueTaskSource.GetResult(_token); } } @@ -722,7 +750,7 @@ public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCaptu { // TODO: Simplify once https://github.com/dotnet/coreclr/pull/16138 is fixed. bool avoidCapture = !continueOnCapturedContext; - return new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _result, _flags | Unsafe.As(ref avoidCapture))); + return new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _result, _token, _flags | Unsafe.As(ref avoidCapture))); } /// Gets a string-representation of this .