Unnecessary thread change during the execution of a sync method which is being advised by an async advice
See original GitHub issueHi,
I found a problem with advising a sync method with an async advice. The problem is that currently an async advice (which implements the IMethodAsyncAdvice
interface) unnecessarily changes the current thread after the execution of the method, in spite of the implementation of the async advice being purely sync (i.e. the advice itself does not do anything async, it just simply proceeds with the advised method).
So currently the following example:
class Program
{
static async Task Main(string[] args)
{
WriteLine($"MyProudMethod BEFORE {Thread.CurrentThread.ManagedThreadId}");
MyProudMethod();
WriteLine($"MyProudMethod AFTER {Thread.CurrentThread.ManagedThreadId}");
WriteLine();
WriteLine($"MyProudMethodAsync BEFORE {Thread.CurrentThread.ManagedThreadId}");
await MyProudMethodAsync();
WriteLine($"MyProudMethodAsync AFTER {Thread.CurrentThread.ManagedThreadId}");
}
[MyProudAdvice]
static void MyProudMethod()
{
WriteLine($"MyProudMethod {Thread.CurrentThread.ManagedThreadId}");
}
[MyProudAdvice]
static async Task MyProudMethodAsync()
{
WriteLine($"MyProudMethodAsync BEGIN {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(1);
WriteLine($"MyProudMethodAsync END {Thread.CurrentThread.ManagedThreadId}");
}
}
public class MyProudAdvice : Attribute, IMethodAsyncAdvice
{
public async Task Advise(MethodAsyncAdviceContext context)
{
WriteLine($"Async Advise BEGIN: {context.TargetMethod.Name} {Thread.CurrentThread.ManagedThreadId}");
await context.ProceedAsync();
WriteLine($"Async Advise END: {context.TargetMethod.Name} {Thread.CurrentThread.ManagedThreadId}");
}
}
shows this output (I edited it, so it includes my comments):
MyProudMethod BEFORE 1
Async Advise BEGIN: MyProudMethod 1
MyProudMethod 1
Async Advise END: MyProudMethod 3 // unnecessary thread change!
MyProudMethod AFTER 1 // here we are back to the original thread
MyProudMethodAsync BEFORE 1
Async Advise BEGIN: MyProudMethodAsync 1
MyProudMethodAsync BEGIN 1
MyProudMethodAsync END 4
Async Advise END: MyProudMethodAsync 4
MyProudMethodAsync AFTER 4
The problem goes away after fixing MrAdvice’s Tasks.Void
method by changing its implementation from this:
public static Task Void()
{
if (_void == null)
{
var @void = new Task(() => { });
@void.Start();
_void = @void;
}
return _void;
}
to this:
public static Task Void()
{
if (_void == null)
{
var tcsVoid = new TaskCompletionSource<object>();
tcsVoid.SetResult(null);
_void = tcsVoid.Task;
}
return _void;
}
Of course, the best solution would be to use .NET’s Task.CompletedTask
method, but I know that it does not exist in .NET 4.0, that is why Tasks.Void
had to be introduced.
After the fix the previous example shows this output, where there are no unnecessary thread changes during the execution of the sync method:
MyProudMethod BEFORE 1
Async Advise BEGIN: MyProudMethod 1
MyProudMethod 1
Async Advise END: MyProudMethod 1
MyProudMethod AFTER 1
MyProudMethodAsync BEFORE 1
Async Advise BEGIN: MyProudMethodAsync 1
MyProudMethodAsync BEGIN 1
MyProudMethodAsync END 4
Async Advise END: MyProudMethodAsync 4
MyProudMethodAsync AFTER 4
Issue Analytics
- State:
- Created 6 years ago
- Comments:6 (4 by maintainers)
Top GitHub Comments
Finally released! Thanks again!
It was some kind of weaving related error: the building of the test projects failed, because it did not find the build/MrAdvice.Weaver.exe (despite that it was definitely there) or the task inside (unfortunately, I do not remember the details), and it said that I should check the
UsingTask
element.I think that the VS restart and solution reload solved the problem eventually.