Open sandboxFocusImprove this doc

THR039: Async Method of an Actor Has an Unsupported Return Type

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 Task instead of a ValueTask, or a Task<T> instead of a ValueTask<T>. The callers that await the 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

Actor Threading Model