Skip to content

Commit 6631be7

Browse files
committed
Avoid duplicate ValueTask checks in transparent awaits
Classify ValueTask backing objects once in TransparentAwait and pass typed Task or IValueTaskSource instances to the suspend helpers. This removes repeated type checks and temporary ValueTask materialization from runtime-async tail awaits. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1d56f651-7596-4378-81ee-21221f3c746e
1 parent e977050 commit 6631be7

1 file changed

Lines changed: 66 additions & 82 deletions

File tree

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs

Lines changed: 66 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -435,64 +435,30 @@ private static unsafe T Suspend<T>(Task<T> task, ConfigureAwaitOptions options)
435435
return default!;
436436
}
437437

438-
/// <summary>
439-
/// Used by internal thunks that implement awaiting on ValueTask.
440-
/// A ValueTask may wrap:
441-
/// - Completed result (we never await this)
442-
/// - Task
443-
/// - ValueTaskSource
444-
/// Therefore, when we are awaiting a ValueTask completion we are really
445-
/// awaiting a completion of an underlying Task or ValueTaskSource.
446-
/// </summary>
447-
/// <param name="valueTask">ValueTask whose completion we are awaiting.</param>
448438
[Intrinsic]
449439
[BypassReadyToRun]
450440
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
451-
private static unsafe void TransparentSuspend(ValueTask valueTask)
441+
private static unsafe void TransparentSuspend(IValueTaskSource source, short token)
452442
{
453443
ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
454444
Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation();
455445

456-
Continuation nextCont;
457-
object? obj = valueTask._obj;
458-
if (obj is Task t)
446+
ValueTaskSourceContinuation? vtsCont = state.CachedValueTaskSourceContinuation;
447+
if (vtsCont != null)
459448
{
460-
RuntimeAsyncTaskContinuation? taskCont = state.CachedTaskContinuation;
461-
if (taskCont != null)
462-
{
463-
state.CachedTaskContinuation = null;
464-
}
465-
else
466-
{
467-
taskCont = new RuntimeAsyncTaskContinuation();
468-
}
469-
470-
taskCont.Initialize(t);
471-
state.StackState->TaskContinuation = taskCont;
472-
nextCont = taskCont;
449+
state.CachedValueTaskSourceContinuation = null;
473450
}
474451
else
475452
{
476-
ValueTaskSourceContinuation? vtsCont = state.CachedValueTaskSourceContinuation;
477-
if (vtsCont != null)
478-
{
479-
state.CachedValueTaskSourceContinuation = null;
480-
}
481-
else
482-
{
483-
vtsCont = new ValueTaskSourceContinuation();
484-
}
485-
486-
Debug.Assert(obj is IValueTaskSource);
487-
vtsCont.Initialize(Unsafe.As<object, IValueTaskSource>(ref obj), valueTask._token);
488-
state.StackState->ValueTaskSourceContinuation = vtsCont;
489-
nextCont = vtsCont;
453+
vtsCont = new ValueTaskSourceContinuation();
490454
}
491455

492-
sentinelContinuation.Next = nextCont;
456+
vtsCont.Initialize(source, token);
493457

458+
sentinelContinuation.Next = vtsCont;
459+
state.StackState->ValueTaskSourceContinuation = vtsCont;
494460
state.CaptureContexts();
495-
AsyncSuspend(nextCont);
461+
AsyncSuspend(vtsCont);
496462
}
497463

498464
[Intrinsic]
@@ -536,50 +502,27 @@ private static unsafe void Suspend(IValueTaskSource source, short token, bool co
536502
[Intrinsic]
537503
[BypassReadyToRun]
538504
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)]
539-
private static unsafe T TransparentSuspend<T>(ValueTask<T> valueTask)
505+
private static unsafe T TransparentSuspend<T>(IValueTaskSource<T> source, short token)
540506
{
541507
ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState;
542508
Continuation? sentinelContinuation = state.SentinelContinuation ??= new Continuation();
543509

544-
Continuation nextCont;
545-
object? obj = valueTask._obj;
546-
if (obj is Task<T> t)
510+
ValueTaskSourceContinuation? vtsCont = state.CachedValueTaskSourceContinuation;
511+
if (vtsCont != null)
547512
{
548-
RuntimeAsyncTaskContinuation? taskCont = state.CachedTaskContinuation;
549-
if (taskCont != null)
550-
{
551-
state.CachedTaskContinuation = null;
552-
}
553-
else
554-
{
555-
taskCont = new RuntimeAsyncTaskContinuation();
556-
}
557-
558-
taskCont.Initialize<T>(t);
559-
state.StackState->TaskContinuation = taskCont;
560-
nextCont = taskCont;
513+
state.CachedValueTaskSourceContinuation = null;
561514
}
562515
else
563516
{
564-
ValueTaskSourceContinuation? vtsCont = state.CachedValueTaskSourceContinuation;
565-
if (vtsCont != null)
566-
{
567-
state.CachedValueTaskSourceContinuation = null;
568-
}
569-
else
570-
{
571-
vtsCont = new ValueTaskSourceContinuation();
572-
}
573-
574-
Debug.Assert(obj is IValueTaskSource<T>);
575-
vtsCont.Initialize<T>(Unsafe.As<object, IValueTaskSource<T>>(ref obj), valueTask._token);
576-
state.StackState->ValueTaskSourceContinuation = vtsCont;
577-
nextCont = vtsCont;
517+
vtsCont = new ValueTaskSourceContinuation();
578518
}
579519

580-
sentinelContinuation.Next = nextCont;
520+
vtsCont.Initialize<T>(source, token);
521+
522+
sentinelContinuation.Next = vtsCont;
523+
state.StackState->ValueTaskSourceContinuation = vtsCont;
581524
state.CaptureContexts();
582-
AsyncSuspend(nextCont);
525+
AsyncSuspend(vtsCont);
583526
return default!;
584527
}
585528

@@ -706,14 +649,35 @@ private static void TransparentAwait(Task task)
706649
[MethodImpl(MethodImplOptions.Async)]
707650
private static void TransparentAwait(ValueTask task)
708651
{
709-
if (!task.IsCompleted)
652+
object? obj = task._obj;
653+
if (obj == null)
654+
{
655+
return;
656+
}
657+
658+
if (obj is Task t)
659+
{
660+
if (!t.IsCompleted)
661+
{
662+
TailAwait();
663+
TransparentSuspend(t);
664+
return;
665+
}
666+
667+
TaskAwaiter.ValidateEnd(t);
668+
return;
669+
}
670+
671+
Debug.Assert(obj is IValueTaskSource);
672+
IValueTaskSource vts = Unsafe.As<object, IValueTaskSource>(ref obj);
673+
if (vts.GetStatus(task._token) == ValueTaskSourceStatus.Pending)
710674
{
711675
TailAwait();
712-
TransparentSuspend(task);
676+
TransparentSuspend(vts, task._token);
713677
return;
714678
}
715679

716-
task.ThrowIfCompletedUnsuccessfully();
680+
vts.GetResult(task._token);
717681
}
718682

719683
[BypassReadyToRun]
@@ -734,13 +698,33 @@ private static T TransparentAwait<T>(Task<T> task)
734698
[MethodImpl(MethodImplOptions.Async)]
735699
private static T TransparentAwait<T>(ValueTask<T> task)
736700
{
737-
if (!task.IsCompleted)
701+
object? obj = task._obj;
702+
if (obj == null)
703+
{
704+
return task._result!;
705+
}
706+
707+
if (obj is Task<T> t)
708+
{
709+
if (!t.IsCompleted)
710+
{
711+
TailAwait();
712+
return TransparentSuspend(t);
713+
}
714+
715+
TaskAwaiter.ValidateEnd(t);
716+
return t.ResultOnSuccess;
717+
}
718+
719+
Debug.Assert(obj is IValueTaskSource<T>);
720+
IValueTaskSource<T> vts = Unsafe.As<object, IValueTaskSource<T>>(ref obj);
721+
if (vts.GetStatus(task._token) == ValueTaskSourceStatus.Pending)
738722
{
739723
TailAwait();
740-
return TransparentSuspend(task);
724+
return TransparentSuspend(vts, task._token);
741725
}
742726

743-
return task.Result;
727+
return vts.GetResult(task._token);
744728
}
745729

746730
// Represents execution of a chain of suspended and resuming runtime

0 commit comments

Comments
 (0)