MethodBoundaryAspect.Fody icon indicating copy to clipboard operation
MethodBoundaryAspect.Fody copied to clipboard

await Async method will create wrong IL

Open jerviscui opened this issue 3 years ago • 4 comments

TestClass:

[TaskLog]
public class TaskTest
{
    public Task Test()
    {
        Console.WriteLine($"front {Thread.CurrentThread.ManagedThreadId}");

        return Task.Delay(1000);
        //Console.WriteLine("end");
    }
    
    public async Task AwaitTest()
    {
        Console.WriteLine($"front {Thread.CurrentThread.ManagedThreadId}");
        await Task.Delay(10);
        Console.WriteLine($"end {Thread.CurrentThread.ManagedThreadId}");
        //throw new ArgumentException();
    }
}

method boundary Aspect:

public class TaskLogAttribute : OnMethodBoundaryAspect
{
    /// <inheritdoc />
    public override void OnEntry(MethodExecutionArgs arg)
    {
        Console.WriteLine($"OnEntry {Thread.CurrentThread.ManagedThreadId}");
    }

    /// <inheritdoc />
    public override void OnExit(MethodExecutionArgs arg)
    {
        Console.WriteLine($"OnExit {Thread.CurrentThread.ManagedThreadId}");
    }
}

caller:

class Program
{
    static async Task Main(string[] args)
    {
        var t = new TaskTest();
        await t.AwaitTest();
    }
}

The result:

OnEntry 4 OnEntry 4 front 4 OnExit 4 OnExit 4 OnEntry 4 end 4 OnExit 4

jerviscui avatar Apr 15 '21 10:04 jerviscui

I think your OnExit method should look like the following:

    /// <inheritdoc />
    public override void OnExit(MethodExecutionArgs arg)
    {
        if (arg.ReturnValue is Task t)
        {
            t.ContinueWith(_ =>
            {
                Console.WriteLine($"OnExit {Thread.CurrentThread.ManagedThreadId}");
            }, TaskContinuationOptions.ExecuteSynchronously);
        }
        else
        {
            Console.WriteLine($"OnExit {Thread.CurrentThread.ManagedThreadId}");
        }
    }

FerdinandBrunauer avatar Mar 18 '22 08:03 FerdinandBrunauer

It's been too long. But, I think I wan to say, OnEntry and OnExit run many times. Beacuse there is many times run OnEntry and OnExit in the TaskStateMachine.

jerviscui avatar Apr 29 '22 08:04 jerviscui

I think the issue here is that there's a difference currently whether an aspect is applied to a class or to a method directly. If it is applied directly to an async method there is special handling of the underlying MoveNext (see e.g. https://github.com/vescon/MethodBoundaryAspect.Fody/blob/24034961ad6e358d089abf16b01ec4bf19d98d7b/src/MethodBoundaryAspect.Fody/MethodWeaverFactory.cs#L28) However, if the aspect is applied to the class (or assembly) then it is also applied to the compiler generated state machine class as well, and thus the MoveNext method contained therein.

mjvh80 avatar Dec 02 '22 11:12 mjvh80

@mjvh80 so the solution would be to identify the compiler generated state machine class and skip weaving anything in it?

Ralf1108 avatar Dec 02 '22 12:12 Ralf1108