Click here to Skip to main content
15,031,207 members
Articles / Programming Languages / C#
Tip/Trick
Posted 2 Jan 2021

Tagged as

Stats

2.8K views
1 bookmarked

How to Await an Asynchronous Method in Emit Code

Rate me:
Please Sign up or sign in to vote.
2.40/5 (2 votes)
2 Jan 2021CPOL1 min read
Awaiting an asynchronous method in Emit code

Preface

First of all, immediately explain why there is such a pseudo-titled Demo essay? It’s not that I have a misunderstanding of knowledge or want to mislead others because everyone knows that emit writes are synchronous methods, it is impossible to await, at least it has not provided corresponding functions for so many years.

Begin with Demo

The original method is a method that returns 55 after a delay of 2 seconds:

C#
public static async Task<int> GetV()
{
    await Task.Delay(2000);
    return 55;
}

Now we need to add 6 to the result of 55 so that the final result becomes 61.

Our test method is like this, it will output some simple time to help us understand the execution order and asynchronous situation.

C#
private static async Task Test(MethodInfo method, MethodInfo awaitMehtod)
{
    var caller = CreateCaller(method, awaitMehtod);
    Console.WriteLine($"Start {awaitMehtod.Name} at: {DateTime.Now}.");
    var task = caller();
    Console.WriteLine($"Call done at: {DateTime.Now}.");
    var number = await task;
    Console.WriteLine($"Hello {number} at: {DateTime.Now}.");
    Console.WriteLine($"End at: {DateTime.Now}.");
    Console.WriteLine();
}

1. ContinueWith

C#
public static Func<Task<int>> CreateCaller(MethodInfo method, MethodInfo awaitMehtod)
 {
     var m = new DynamicMethod(Guid.NewGuid().ToString("N"),
             typeof(Task<int>), Type.EmptyTypes);
     var il = m.GetILGenerator();
     il.Emit(OpCodes.Call, method);
     il.Emit(OpCodes.Call, typeof(Program).GetMethod
            (nameof(Program.AddSixUseContinueWith))); // Here is the difference
     il.Emit(OpCodes.Ret);

     return m.CreateDelegate(typeof(Func<Task<int>>)) as Func<Task<int>>;
 }

 public static Task<int> AddSixUseContinueWith(Task<int> task)
 {
     return task.ContinueWith(i =>
     {
         Console.WriteLine($"AddSixUseContinueWith is: {DateTime.Now}.");
         return i.Result + 6;
     });
 }

Test Results

Start AddSixUseContinueWith at: 2021/1/2 13:34:55.
Call done at: 2021/1/2 13:34:55.
AddSixUseContinueWith is: 2021/1/2 13:34:57.
Hello 61 at: 2021/1/2 13:34:57.
End at: 2021/1/2 13:34:57.

Advantage

Still really asynchronous

Disadvantage

The cost is relatively large. After all, there is no optimization such as the state machine. (The cost is at the ns level, not the ms you think.)

2. GetAwaiter().GetResult()

C#
public static Func<Task<int>> CreateCaller(MethodInfo method, MethodInfo awaitMehtod)
 {
     var m = new DynamicMethod(Guid.NewGuid().ToString("N"),
             typeof(Task<int>), Type.EmptyTypes);
     var il = m.GetILGenerator();
     il.Emit(OpCodes.Call, method);
     il.Emit(OpCodes.Call, typeof(Program).GetMethod
            (nameof(Program.AddSixUseAwaiter))); // Here is the difference
     il.Emit(OpCodes.Ret);

     return m.CreateDelegate(typeof(Func<Task<int>>)) as Func<Task<int>>;
 }

 public static Task<int> AddSixUseAwaiter(Task<int> task)
 {
     var r = task.ConfigureAwait(false).GetAwaiter().GetResult() + 6;
     Console.WriteLine($"AddSixUseAwaiter is: {DateTime.Now}.");
     return Task.FromResult(r);
 }

Test Results

Start AddSixUseAwaiter at: 2021/1/2 13:34:57.
AddSixUseAwaiter is: 2021/1/2 13:34:59.
Call done at: 2021/1/2 13:34:59.
Hello 61 at: 2021/1/2 13:34:59.
End at: 2021/1/2 13:34:59.

Advantage

Very small execution time

Disadvantage

Of course, in this way, asynchronous has become synchronous, so in some cases, we may operate improper code and lose the advantages of asynchronous methods.

3. async/await

C#
public static Func<Task<int>> CreateCaller(MethodInfo method, MethodInfo awaitMehtod)
 {
     var m = new DynamicMethod(Guid.NewGuid().ToString("N"),
             typeof(Task<int>), Type.EmptyTypes);
     var il = m.GetILGenerator();
     il.Emit(OpCodes.Call, method);
     il.Emit(OpCodes.Call, typeof(Program).GetMethod
            (nameof(Program.AddSixUseAsyncAwait))); // Here is the difference
     il.Emit(OpCodes.Ret);

     return m.CreateDelegate(typeof(Func<Task<int>>)) as Func<Task<int>>;
 }

 public static async Task<int> AddSixUseAsyncAwait(Task<int> task)
 {
     var r = await task;
     Console.WriteLine($"AddSixUseAsyncAwait is: {DateTime.Now}.");
     return r + 6;
 }

Test Results

Start AddSixUseAsyncAwait at: 2021/1/2 13:34:59.
Call done at: 2021/1/2 13:34:59.
AddSixUseAsyncAwait is: 2021/1/2 13:35:01.
Hello 61 at: 2021/1/2 13:35:01.
End at: 2021/1/2 13:35:01.

Advantage

The advantages of async / await are not lost.

Disadvantage

I originally wanted to migrate the result processing logic in emit to the async / await method, and the emit code must be well designed.

All of the Demo On

https://github.com/fs7744/grocery/blob/main/csharp/emit_await/EmitAwaitDemo/Program.cs

Sharing is not easy, if you can give a little motivation, I would be very grateful: pay attention to my open source project:Norns.Urd

History

  • 2nd January, 2021: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

fs7744
Architect
China China
No Biography provided

Comments and Discussions

 
BugWrong implementation? Pin
wmjordan3-Jan-21 13:30
professionalwmjordan3-Jan-21 13:30 
I did not downvote your article.
I just want to remind you that your implementation may probably be wrong.

The await/async pattern is implemented both in the Basic Class Library and the compiler. If you decompile an assembly which contains that pattern or read some articles on the web, you can see that the compiler has modified the workflow of the code by replacing each await with a state machine which utilizes various types in the BCL.

It is not an easy task to do equivalent job via Emit.

EDIT:
I read your explanations to each approaches. Yet none of them has implemented that type of state machine. Thus, you at least should not title your article as "await async methods". Maybe "call async methods" is a more appropriate one.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.