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/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..f4655177f936 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 . /// @@ -377,7 +386,18 @@ internal void ThrowIfCompletedUnsuccessfully() /// [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. @@ -765,15 +785,34 @@ 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 . /// /// 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()