mgravell / mgravell/PooledAwait

ValueTaskCompletionSource: ValidateOptimized called for every T

Open
#12 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
405
Forks
25
PR merge metrics
No merged PRs in 30d

Description

With the current implementation, the `ValidateOptimized` method is called once for every type used as a type parameter. I suspect it only needs to be called once.

Moving most of the static members out of the `ValueTaskCompletionSource` struct would allow this:
```csharp
internal static class TaskHelper
{
#if NETSTANDARD1_3
public static readonly bool UseOptimizedPath = false;
#else
private static class TaskInternals
{
public static readonly Func, T, bool> TrySetResult = TryCreate(nameof(TrySetResult));
public static readonly Func, Exception, bool> TrySetException = TryCreate(nameof(TrySetException));
public static readonly Func, CancellationToken, bool> TrySetCanceled = TryCreate(nameof(TrySetCanceled));

[MethodImpl(MethodImplOptions.NoInlining)]
private static Func, TArg, bool> TryCreate(string methodName)
{
try
{
var method = typeof(Task).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(TArg) }, null);
return method is null ? null : (Func, TArg, bool>)Delegate.CreateDelegate(typeof(Func, TArg, bool>), method);
}
catch
{
return null;
}
}
}

public static readonly bool UseOptimizedPath = ValidateOptimized();

[MethodImpl(MethodImplOptions.NoInlining)]
private static bool ValidateOptimized()
{
try
{
if (TaskInternals.TrySetResult is null) return false;
if (TaskInternals.TrySetException is null) return false;
if (TaskInternals.TrySetCanceled is null) return false;

var task = CreateUninitializedTask();
if (task is null) return false;
if (task.IsCompleted) return false;

if (!TaskInternals.TrySetResult(task, default)) return false;
if (task.Status != TaskStatus.RanToCompletion) return false;

task = CreateUninitializedTask();
if (!TaskInternals.TrySetException(task, new InvalidOperationException())) return false;
if (!task.IsCompleted) return false;
if (!task.IsFaulted) return false;

try
{
_ = task.Result;
return false;
}
catch (AggregateException ex) when (ex.InnerException is InvalidOperationException)
{
}

return task.Exception?.InnerException is InvalidOperationException;
}
catch
{
return false;
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void SpinUntilCompleted([NotNull] Task task)
{
// Spin wait until the completion is finalized by another thread.
var sw = new SpinWait();
while (!task.IsCompleted)
{
sw.SpinOnce();
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Task CreateUninitializedTask() => (Task)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(Task));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TrySetResult(this Task task, T value)
{
bool result = TaskInternals.TrySetResult(task, value);
if (!result && !task.IsCompleted) SpinUntilCompleted(task);
return result;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TrySetException(this Task task, Exception error)
{
bool result = TaskInternals.TrySetException(task, error);
if (!result && !task.IsCompleted) SpinUntilCompleted(task);
return result;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TrySetCanceled(this Task task, CancellationToken cancellationToken)
{
bool result = TaskInternals.TrySetCanceled(task, cancellationToken);
if (!result && !task.IsCompleted) SpinUntilCompleted(task);
return result;
}
#endif
}
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by locating ValueTaskCompletionSource and its ValidateOptimized method. Compare how its static members are initialized with the proposed TaskHelper and TaskInternals structure. Done means ValidateOptimized runs once rather than once per type parameter while the optimized task completion behavior remains valid.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.