| Severity | Error |
| Reported by | PostSharp.Patterns.Threading |
| Message | Async method "<Method>" must have the return type void, Task or Task<T> because its declaring class has the [Actor] threading model and the method is an entry point of the actor, such as a public or internal method. Async methods with other return types, such as ValueTask<T>, are not supported. |
Cause
The ActorAttribute threading model dispatches every call to an entry point of the actor to the queue of the actor, so that the actor processes one call at a time. The entry points are the public, internal and protected internal methods, the implementations of interface methods, and the methods annotated with EntryPointAttribute.
The actor can only dispatch an async method that returns void, Task or Task<T>. PostSharp reports THR039 when an async entry point returns another awaitable type, for example ValueTask or ValueTask<T>.
In the following example, the GetPriceAsync method returns a ValueTask<int>. The method is annotated with ReentrantAttribute because the actor requires it on every async method, which PostSharp otherwise reports with THR017.
using System;
using System.Threading.Tasks;
using PostSharp.Patterns.Threading;
namespace Errors.THR039_Error;
[Actor]
public class PriceCalculator
{
private int requestCount;
// The actor cannot dispatch an async method that returns a ValueTask<T>.
[Reentrant]
public async ValueTask<int> GetPriceAsync( int quantity )
{
await Task.Yield();
this.requestCount++;
return quantity * 5;
}
}
public static class Program
{
public static async Task Main()
{
var calculator = new PriceCalculator();
Console.WriteLine( $"Price: {await calculator.GetPriceAsync( 3 )}" );
}
}
error THR039: THR039_Error.cs(14,33): Async method "Errors.THR039_Error.PriceCalculator.GetPriceAsync(System.Int32)" must have the return type void, Task or Task<T> because its declaring class has the [Actor] threading model and the method is an entry point of the actor, such as a public or internal method. Async methods with other return types, such as ValueTask<T>, are not supported.
Resolution
- Return a
Taskinstead of aValueTask, or aTask<T>instead of aValueTask<T>. The callers thatawaitthe method do not change. - When the method does not need to be called from outside the actor, make it private, so that it is not an entry point and the actor does not dispatch it.
In the following example, GetPriceAsync returns a Task<int>.
using System;
using System.Threading.Tasks;
using PostSharp.Patterns.Threading;
namespace Errors.THR039_Fixed;
[Actor]
public class PriceCalculator
{
private int requestCount;
// The method returns a Task<T>, which the actor can dispatch.
[Reentrant]
public async Task<int> GetPriceAsync( int quantity )
{
await Task.Yield();
this.requestCount++;
return quantity * 5;
}
}
public static class Program
{
public static async Task Main()
{
var calculator = new PriceCalculator();
Console.WriteLine( $"Price: {await calculator.GetPriceAsync( 3 )}" );
}
}
Price: 15
See Also
Reference
ActorAttribute
ReentrantAttribute
EntryPointAttribute
Other Resources