MethodBoundaryAspect.Fody
MethodBoundaryAspect.Fody copied to clipboard
await Async method will create wrong IL
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
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}");
}
}
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.
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 so the solution would be to identify the compiler generated state machine class and skip weaving anything in it?