Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / .NET

Internal Implementation of Async Methods in .NET [#2]

5.00/5 (3 votes)
22 Feb 2015CPL2 min read 7K  
Internal Implementation of Async Methods in .NET [#2]

The State Machine in Async

Previously [Part 1] The Stub Method of Async

The state machine does exactly what it is supposed to – keeps track of the state when the execution reaches await. When await is reached, everything about where we are in the method is preserved, so that it can be resumed when the method is awaiting.

Instead of copying all local variables, which could potentially result in a lot of generated code, the compiler changes all the local variables to member variables and stores the instance of the type. Obviously, in case of static async methods, there is no instance, so it would be omitted. The state machine is generated as an inner struct nested under the type, giving it access to the private members of that type.

Below is the example of what member variables would be generated for <GetStringAsync>d__0:

C#
public int <>1__state;
public AsyncTaskMethodBuilder<string> <>t__builder;
public AsyncStringClass <>4__this;
public string value;
public string <result>5__1;
private TaskAwaiter <>u__$awaiter2;
private object <>t__stack;

Description of Member Variables

The <>1__state variable to store the await count we have reached. Initially, before any await is reached, its value is -1. Each await in async method is numbered and the number of the current await is written to this member variable.

Next is the <>t__builder, which is helper type containing the logic that all state machines share. It creates the Task that is returned by the method. The Task is created similarly to how you would create a Task from TaskCompletonSource. The only difference here is that it is optimised for async methods and is implemented as a struct for performance reasons. If you are also curious about the implementation details of AsyncTaskMethodBuilder the source code, again can be found here.

You would notice that this file contains some other helper types.

The <>__this variable contains the object that contains our async method. For static async methods, this variable will not be generated (as stated previously). The knowledge of the type will suffice. After the async transformation, it needs to be stored and the code would have moved away from its original method and the object.

The value variable is nothing more than the reference to our string (I almost wrote a ‘copy of our string’ here). All accesses to our value method parameter will be replaced by value stored in the generated struct.

Exactly the same logic applies to the result method variable, which is stored in the struct as <result>5__1.

The variable <>u_$awaiter2 is a variable of type TaskAwaiter. It is used as a temporary storage for the object that is used by the await keyword to sign up for notification when the Task finishes.

Finally note, that the state machine keeps track of the stack in the member variable <>t__stack. All the current values are placed in this variable. If there is more than 1, then the values are placed inside a Tuple.

License

This article, along with any associated source code and files, is licensed under The Common Public License Version 1.0 (CPL)