From 719dc69c680e15051ec94a9509f5d0407a7398f0 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 21 Feb 2019 02:00:13 +0000 Subject: [PATCH 1/2] Reduce struct copies in ValueTask.GetAwaiter --- .../CompilerServices/ValueTaskAwaiter.cs | 20 ++++++++++++++++-- .../System/Threading/Tasks/ValueTask.cs | 21 +++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs index 56d8bca99d43..c851222defee 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ValueTaskAwaiter.cs @@ -30,10 +30,18 @@ namespace System.Runtime.CompilerServices /// The value being awaited. private readonly ValueTask _value; + // NOTE: If any extra fields are added ValueTask.GetAwaiter + // must be updated to call the constructor. + /// Initializes the awaiter. /// The value to be awaited. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ValueTaskAwaiter(ValueTask value) => _value = value; + internal ValueTaskAwaiter(ValueTask value) + { + // NOTE: If this method is ever updated to perform more initialization, + // ValueTask.GetAwaiter must be updated to call this. + _value = value; + } /// Gets whether the has completed. public bool IsCompleted @@ -113,10 +121,18 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b /// The value being awaited. private readonly ValueTask _value; + // NOTE: If any extra fields are added ValueTask.GetAwaiter + // must be updated to call the constructor. + /// Initializes the awaiter. /// The value to be awaited. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ValueTaskAwaiter(ValueTask value) => _value = value; + internal ValueTaskAwaiter(ValueTask value) + { + // NOTE: If this method is ever updated to perform more initialization, + // ValueTask.GetAwaiter must be updated to call this. + _value = value; + } /// Gets whether the has completed. public bool IsCompleted 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 256d4d71a2c3..81a6a9984ff4 100644 --- a/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs @@ -369,7 +369,16 @@ internal void ThrowIfCompletedUnsuccessfully() } /// Gets an awaiter for this . - public ValueTaskAwaiter GetAwaiter() => new ValueTaskAwaiter(this); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ValueTaskAwaiter GetAwaiter() + { + // Unsafe casting to work around https://github.com/dotnet/coreclr/issues/18542 + + // NOTE: This only works because ValueTaskAwaiter and ValueTask are structs, + // exactly the same data layout and ValueTaskAwaiter does no real initalization. + + return Unsafe.As(ref Unsafe.AsRef(in this)); + } /// Configures an awaiter for this . /// @@ -765,7 +774,15 @@ public TResult Result /// Gets an awaiter for this . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTaskAwaiter GetAwaiter() => new ValueTaskAwaiter(this); + public ValueTaskAwaiter GetAwaiter() + { + // Unsafe casting to work around https://github.com/dotnet/coreclr/issues/18542 + + // NOTE: This only works because ValueTaskAwaiter and ValueTask are structs, + // exactly the same data layout and ValueTaskAwaiter.ctor does no real initalization. + + return Unsafe.As, ValueTaskAwaiter>(ref Unsafe.AsRef(in this)); + } /// Configures an awaiter for this . /// From 9804abe199698e52217b1111318ea0f91a883288 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 21 Feb 2019 04:44:16 +0000 Subject: [PATCH 2/2] Include ConfiguredValueTask --- .../ConfiguredValueTaskAwaitable.cs | 20 +++++++++++-- .../System/Threading/Tasks/ValueTask.cs | 28 +++++++++++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs index 66ad43e14567..6cfe8ef11db9 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs @@ -28,7 +28,15 @@ public readonly struct ConfiguredValueTaskAwaitable /// Returns an awaiter for this instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ConfiguredValueTaskAwaiter GetAwaiter() => new ConfiguredValueTaskAwaiter(_value); + public ConfiguredValueTaskAwaiter GetAwaiter() + { + // Unsafe casting to work around https://github.com/dotnet/coreclr/issues/18542 + + // NOTE: This only works because ConfiguredValueTaskAwaiter and ConfiguredValueTaskAwaitable are structs, + // exactly the same data layout and ConfiguredValueTaskAwaitable.ctor does no real initalization. + + return Unsafe.As(ref Unsafe.AsRef(in this)); + } /// Provides an awaiter for a . [StructLayout(LayoutKind.Auto)] @@ -134,7 +142,15 @@ public readonly struct ConfiguredValueTaskAwaitable /// Returns an awaiter for this instance. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ConfiguredValueTaskAwaiter GetAwaiter() => new ConfiguredValueTaskAwaiter(_value); + public ConfiguredValueTaskAwaiter GetAwaiter() + { + // Unsafe casting to work around https://github.com/dotnet/coreclr/issues/18542 + + // NOTE: This only works because ConfiguredValueTaskAwaiter and ConfiguredValueTaskAwaitable are structs, + // exactly the same data layout and ConfiguredValueTaskAwaitable.ctor does no real initalization. + + return Unsafe.As, ConfiguredValueTaskAwaiter>(ref Unsafe.AsRef(in this)); + } /// Provides an awaiter for a . [StructLayout(LayoutKind.Auto)] 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 81a6a9984ff4..f4655177f936 100644 --- a/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs +++ b/src/System.Private.CoreLib/shared/System/Threading/Tasks/ValueTask.cs @@ -386,7 +386,18 @@ public ValueTaskAwaiter GetAwaiter() /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) => - new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _token, continueOnCapturedContext)); + ConfigureTask(new ValueTask(_obj, _token, continueOnCapturedContext)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ref readonly ConfiguredValueTaskAwaitable ConfigureTask(in ValueTask vt) + { + // Unsafe casting to work around https://github.com/dotnet/coreclr/issues/18542 + + // NOTE: This only works because ConfiguredValueTaskAwaitable and ValueTask are structs, + // exactly the same data layout and ConfiguredValueTaskAwaitable.ctor does no real initalization. + + return ref Unsafe.As(ref Unsafe.AsRef(in vt)); + } } /// Provides a value type that can represent a synchronously available value or a task object. @@ -789,8 +800,19 @@ public ValueTaskAwaiter GetAwaiter() /// true to attempt to marshal the continuation back to the captured context; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) => - new ConfiguredValueTaskAwaitable(new ValueTask(_obj, _result, _token, continueOnCapturedContext)); + public ConfiguredValueTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) + => ConfigureTask(new ValueTask(_obj, _result, _token, continueOnCapturedContext)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ref readonly ConfiguredValueTaskAwaitable ConfigureTask(in ValueTask vt) + { + // Unsafe casting to work around https://github.com/dotnet/coreclr/issues/18542 + + // NOTE: This only works because ConfiguredValueTaskAwaitable and ValueTask are structs, + // exactly the same data layout and ConfiguredValueTaskAwaitable.ctor does no real initalization. + + return ref Unsafe.As, ConfiguredValueTaskAwaitable>(ref Unsafe.AsRef(in vt)); + } /// Gets a string-representation of this . public override string ToString()