Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Reduce struct copies in ValueTask.GetAwaiter #22735

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ public readonly struct ConfiguredValueTaskAwaitable

/// <summary>Returns an awaiter for this <see cref="ConfiguredValueTaskAwaitable"/> instance.</summary>
[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<ConfiguredValueTaskAwaitable, ConfiguredValueTaskAwaiter>(ref Unsafe.AsRef(in this));
}

/// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable"/>.</summary>
[StructLayout(LayoutKind.Auto)]
Expand Down Expand Up @@ -134,7 +142,15 @@ public readonly struct ConfiguredValueTaskAwaitable<TResult>

/// <summary>Returns an awaiter for this <see cref="ConfiguredValueTaskAwaitable{TResult}"/> instance.</summary>
[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<ConfiguredValueTaskAwaitable<TResult>, ConfiguredValueTaskAwaiter>(ref Unsafe.AsRef(in this));
}

/// <summary>Provides an awaiter for a <see cref="ConfiguredValueTaskAwaitable{TResult}"/>.</summary>
[StructLayout(LayoutKind.Auto)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,18 @@ namespace System.Runtime.CompilerServices
/// <summary>The value being awaited.</summary>
private readonly ValueTask _value;

// NOTE: If any extra fields are added ValueTask.GetAwaiter
// must be updated to call the constructor.

/// <summary>Initializes the awaiter.</summary>
/// <param name="value">The value to be awaited.</param>
[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<TResult>.GetAwaiter must be updated to call this.
_value = value;
}

/// <summary>Gets whether the <see cref="ValueTask"/> has completed.</summary>
public bool IsCompleted
Expand Down Expand Up @@ -113,10 +121,18 @@ void IStateMachineBoxAwareAwaiter.AwaitUnsafeOnCompleted(IAsyncStateMachineBox b
/// <summary>The value being awaited.</summary>
private readonly ValueTask<TResult> _value;

// NOTE: If any extra fields are added ValueTask<TResult>.GetAwaiter
// must be updated to call the constructor.

/// <summary>Initializes the awaiter.</summary>
/// <param name="value">The value to be awaited.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ValueTaskAwaiter(ValueTask<TResult> value) => _value = value;
internal ValueTaskAwaiter(ValueTask<TResult> value)
{
// NOTE: If this method is ever updated to perform more initialization,
// ValueTask<TResult>.GetAwaiter must be updated to call this.
_value = value;
}

/// <summary>Gets whether the <see cref="ValueTask{TResult}"/> has completed.</summary>
public bool IsCompleted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,35 @@ internal void ThrowIfCompletedUnsuccessfully()
}

/// <summary>Gets an awaiter for this <see cref="ValueTask"/>.</summary>
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<ValueTask, ValueTaskAwaiter>(ref Unsafe.AsRef(in 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));
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<ValueTask, ConfiguredValueTaskAwaitable>(ref Unsafe.AsRef(in vt));
}
}

/// <summary>Provides a value type that can represent a synchronously available value or a task object.</summary>
Expand Down Expand Up @@ -765,15 +785,34 @@ public TResult Result

/// <summary>Gets an awaiter for this <see cref="ValueTask{TResult}"/>.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ValueTaskAwaiter<TResult> GetAwaiter() => new ValueTaskAwaiter<TResult>(this);
public ValueTaskAwaiter<TResult> GetAwaiter()
{
// Unsafe casting to work around https://github.com/dotnet/coreclr/issues/18542

// NOTE: This only works because ValueTaskAwaiter<TResult> and ValueTask<TResult> are structs,
// exactly the same data layout and ValueTaskAwaiter.ctor does no real initalization.

return Unsafe.As<ValueTask<TResult>, ValueTaskAwaiter<TResult>>(ref Unsafe.AsRef(in this));
}

/// <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>(new ValueTask<TResult>(_obj, _result, _token, continueOnCapturedContext));
public ConfiguredValueTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext)
=> ConfigureTask(new ValueTask<TResult>(_obj, _result, _token, continueOnCapturedContext));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ref readonly ConfiguredValueTaskAwaitable<TResult> ConfigureTask(in ValueTask<TResult> 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<ValueTask<TResult>, ConfiguredValueTaskAwaitable<TResult>>(ref Unsafe.AsRef(in vt));
}

/// <summary>Gets a string-representation of this <see cref="ValueTask{TResult}"/>.</summary>
public override string ToString()
Expand Down