Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dangling await and possible memory leaks in async programming

The flow containing await in .NET 4.5 and Async CTP 4.0 can be stuck due to various reasons, e.g. since the remote client has not responded. Of course, WaitForAny, when we wait also for some timeout task is an obvious solution for recovery of the high-level flow. Still, this does not solve all possible problems.

I have the following questions:

  1. What happens to the context of await which does not ever return? I understand that this will create memory leak. Am I right?

  2. How can I check either in debugger or using the respective API how many dangling "awaiter"s exist in the application?

  3. Is it possible to enumerate them globally?

  4. If 3. is correct, is it possible to force cancellation the tasks for these *await*s (i.e. to clean up)?

Note: In question 4 I don't ask about cancellation items to be used during explicit task creation. I mean the case when the task was created indirectly:

async Task<bool> SomeTask()
{
   await Something();
   ...
   return true;
}

Motivation for this question:

  1. Trying to avoid memory leaks
  2. Trying to complication of the code with too many cases involving cancellation tokens
  3. In many situations the timeout is not known in advance for each low-level Task, but the high-level flow can use just recovery approach: "We are stuck? Never mind, just clean up and let's start over".
like image 272
Yuri S. Avatar asked Oct 18 '12 11:10

Yuri S.


People also ask

What happens if async method is called without await?

The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.

What is async await in programming?

In computer programming, the async/await pattern is a syntactic feature of many programming languages that allows an asynchronous, non-blocking function to be structured in a way similar to an ordinary synchronous function.

What happens during async await?

An await expression in an async method doesn't block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method. The async and await keywords don't cause additional threads to be created.

What is async await and how does it work?

The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method.


2 Answers

1 What happens to the context of await which does not ever return?

I believe it will cause a memory leak (if you're awaiting an I/O operation). It's best to always complete your Tasks (and this means always having your async methods return sooner or later).

Update from svick's comment: There are situations where this would not cause a memory leak.

2 How can I check either in debugger or using the respective API how many dangling "awaiter"s exist in the application?

I'm not sure if there's an easy way to do this. I believe it should be possible to write a debugger plugin that uses SoS to find existing heap objects that match the pattern of the asynchronous state machines generated by the compiler.

But that's a lot of work for little benefit.

3 Is it possible to enumerate them globally?

Not with the normal APIs.

If 3 is correct, is it possible to force cancellation the tasks for these awaits (i.e. to clean up)?

Even if you could enumerate them at runtime (e.g., via the profiling API), you can't "force" cancellation onto a task. Cancellation is cooperative.


The correct way to deal with this is with standard cancellation. The Task-based Asynchronous Pattern document specifies guidelines for cancelable async methods.

At the lowest level: Many async APIs in the BCL take an optional CancellationToken.

At a middle level: It's common to have an async method take an optional CancellationToken and just pass it on to other async methods.

At the highest level: It's easy to create a CancellationToken that will fire after a given time.

like image 162
Stephen Cleary Avatar answered Nov 06 '22 18:11

Stephen Cleary


About questions 2 and 3 I have no real answer.

  1. I doubt it as long as the resources used in the task are properly disposed when the CLR disposes the task when the application terminates.

Having tasks around that never return is hardly ever a good thing. To avoid this and to answer point 4: tasks can be cancelled.

You need to create a cancellationtoken that you pass to the task. The task is responsible on his own to watch for the status of that cancellation token and throw an exception when cancelled. (Multiple tasks can be cancelled at once using the same token too.)

This article on MSDN shows you to do so.

like image 32
lboshuizen Avatar answered Nov 06 '22 17:11

lboshuizen