Skip to content

Commit d108713

Browse files
authored
[RuntimeAsync] Do not hold on to the last used Continuation in the runtime async tasks. (#121460)
Fixes: #121459 The wrapped continuation is expected to be used only once - when the corresponding async method is resumed. We can and should null-out the continuation while fetching it in the dispatcher. There is no need to keep it referenced from the task once fetched as it may cause "leaks" observable via WeakReference/Finalization and the like. The close analog of this behavior in the async1 is the C# state machine zeroing out pointer-containing fields in the display struct upon completing or faulting the task. https://github.com/dotnet/roslyn/blob/f2df0ad6d3ef30ed033a794e7150a5600be50a95/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs#L186 https://github.com/dotnet/roslyn/blob/f2df0ad6d3ef30ed033a794e7150a5600be50a95/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs#L224
1 parent 9155f26 commit d108713

File tree

1 file changed

+18
-4
lines changed

1 file changed

+18
-4
lines changed

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ private static void TransparentAwait(object o)
238238
private interface IRuntimeAsyncTaskOps<T>
239239
{
240240
static abstract Action GetContinuationAction(T task);
241-
static abstract Continuation GetContinuationState(T task);
241+
static abstract Continuation MoveContinuationState(T task);
242242
static abstract void SetContinuationState(T task, Continuation value);
243243
static abstract bool SetCompleted(T task);
244244
static abstract void PostToSyncContext(T task, SynchronizationContext syncCtx);
@@ -297,9 +297,16 @@ void ITaskCompletionAction.Invoke(Task completingTask)
297297
private struct Ops : IRuntimeAsyncTaskOps<RuntimeAsyncTask<T>>
298298
{
299299
public static Action GetContinuationAction(RuntimeAsyncTask<T> task) => (Action)task.m_action!;
300-
public static Continuation GetContinuationState(RuntimeAsyncTask<T> task) => (Continuation)task.m_stateObject!;
300+
public static Continuation MoveContinuationState(RuntimeAsyncTask<T> task)
301+
{
302+
Continuation continuation = (Continuation)task.m_stateObject!;
303+
task.m_stateObject = null;
304+
return continuation;
305+
}
306+
301307
public static void SetContinuationState(RuntimeAsyncTask<T> task, Continuation value)
302308
{
309+
Debug.Assert(task.m_stateObject == null);
303310
task.m_stateObject = value;
304311
}
305312

@@ -373,9 +380,16 @@ void ITaskCompletionAction.Invoke(Task completingTask)
373380
private struct Ops : IRuntimeAsyncTaskOps<RuntimeAsyncTask>
374381
{
375382
public static Action GetContinuationAction(RuntimeAsyncTask task) => (Action)task.m_action!;
376-
public static Continuation GetContinuationState(RuntimeAsyncTask task) => (Continuation)task.m_stateObject!;
383+
public static Continuation MoveContinuationState(RuntimeAsyncTask task)
384+
{
385+
Continuation continuation = (Continuation)task.m_stateObject!;
386+
task.m_stateObject = null;
387+
return continuation;
388+
}
389+
377390
public static void SetContinuationState(RuntimeAsyncTask task, Continuation value)
378391
{
392+
Debug.Assert(task.m_stateObject == null);
379393
task.m_stateObject = value;
380394
}
381395

@@ -429,7 +443,7 @@ public static unsafe void DispatchContinuations<T, TOps>(T task) where T : Task,
429443

430444
DispatcherInfo dispatcherInfo;
431445
dispatcherInfo.Next = t_dispatcherInfo;
432-
dispatcherInfo.NextContinuation = TOps.GetContinuationState(task);
446+
dispatcherInfo.NextContinuation = TOps.MoveContinuationState(task);
433447
t_dispatcherInfo = &dispatcherInfo;
434448

435449
while (true)

0 commit comments

Comments
 (0)