Open sandboxFocusImprove this doc

THR009: Actor Method Has a By-Reference Parameter

Severity Error
Reported by PostSharp.Patterns.Threading
Message Method "<Method>" cannot have ref, out or in parameters because its declaring class has the [Actor] threading model and the method can be invoked from outside the object. Return the value through a Task<T> instead.

Cause

The ActorAttribute aspect does not execute a call to an actor in the thread of the caller: it adds the call to the message queue of the actor, which executes the calls one after the other. A by-reference parameter refers to a variable of the caller, which the actor would read or write later, from another thread. PostSharp therefore reports THR009 for a method of an actor that has a ref, out or in parameter and is an entry point of the object, that is, a method that can be invoked from outside the object:

  • a public, internal or protected internal method;
  • a method that implements an interface;
  • a protected method that overrides a method of a base class without a threading model;
  • a method with the EntryPointAttribute custom attribute.

Private methods are not dispatched, because only the actor calls them, so they can have by-reference parameters.

In the following example, the GetReading method of the TemperatureSensor actor returns its value through an out parameter. PostSharp reports the message without a location.

using System;
using PostSharp.Patterns.Threading;

namespace Errors.THR009_Error;

[Actor]
public class TemperatureSensor
{
    private double lastReading = 21.5;

    // The caller does not wait for the actor, so it cannot receive the value of an out parameter.
    public void GetReading( out double reading )
    {
        reading = this.lastReading;
    }
}

public static class Program
{
    public static void Main()
    {
        var sensor = new TemperatureSensor();
        sensor.GetReading( out var reading );
        Console.WriteLine( $"Temperature: {reading}" );
    }
}
error THR009: THR009_Error.cs(12,17): Method "Errors.THR009_Error.TemperatureSensor.GetReading(System.Double@)" cannot have ref, out or in parameters because its declaring class has the [Actor] threading model and the method can be invoked from outside the object. Return the value through a Task<T> instead.

Resolution

  • To return a value, make the method async and return a Task<T>, which the caller awaits. In an actor, an async method must also have the ReentrantAttribute custom attribute; otherwise, PostSharp reports THR017.
  • To pass several values to the actor or receive several values from it, group them in a type instead of using by-reference parameters.
  • When the method does not need to be an entry point of the actor, make it private.

In the following example, GetReadingAsync returns the reading through a Task<double>.

using System;
using System.Threading.Tasks;
using PostSharp.Patterns.Threading;

namespace Errors.THR009_Fixed;

[Actor]
public class TemperatureSensor
{
    private double lastReading = 21.5;

    // The actor returns the value through a task, which the caller awaits.
    [Reentrant]
    public async Task<double> GetReadingAsync()
    {
        await Task.Yield();

        return this.lastReading;
    }
}

public static class Program
{
    public static async Task Main()
    {
        var sensor = new TemperatureSensor();
        var reading = await sensor.GetReadingAsync();
        Console.WriteLine( $"Temperature: {reading}" );
    }
}
Temperature: 21.5

See Also

Reference

ActorAttribute
ReentrantAttribute
EntryPointAttribute

Other Resources

Actor Threading Model