Open sandboxFocusImprove this doc

THR022: Reentrant Method Is Not Async

Severity Error
Reported by PostSharp.Patterns.Threading
Message Cannot apply [Reentrant] to "<Method>" because the method is not async.

Cause

The ReentrantAttribute attribute means that an async method can be re-entered at each await: with a lock-based threading model such as SynchronizedAttribute, the lock of the object is released while the method awaits; with ActorAttribute, other methods of the actor can run in the meantime. A method that is not async has no await at which this could happen.

PostSharp reports THR022 when the attribute is applied to a method that is not async, including a method that returns a Task without the async keyword. PostSharp does not check a method that also has the ExplicitlySynchronizedAttribute attribute.

In the following example, the ArchiveAsync method returns a Task, but it is not async.

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

namespace Errors.THR022_Error;

[Synchronized]
public class OrderArchive
{
    private int archivedCount;

    public int ArchivedCount => this.archivedCount;

    // The method returns a Task, but it is not async.
    [Reentrant]
    public Task ArchiveAsync( string order )
    {
        this.archivedCount++;

        return Task.Delay( 10 );
    }
}

public static class Program
{
    public static void Main()
    {
        var archive = new OrderArchive();
        archive.ArchiveAsync( "Order 1" ).Wait();
        archive.ArchiveAsync( "Order 2" ).Wait();
        Console.WriteLine( $"Archived orders: {archive.ArchivedCount}" );
    }
}
error THR022: THR022_Error.cs(16,17): Cannot apply [Reentrant] to "Errors.THR022_Error.OrderArchive.ArchiveAsync(System.String)" because the method is not async.

Resolution

  • When the method awaits a task, make it async and await the task, so that the object can be re-entered while it waits.
  • Otherwise, remove the [Reentrant] attribute: a method that is not async does not need it.

In the following example, the ArchiveAsync method is async.

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

namespace Errors.THR022_Fixed;

[Synchronized]
public class OrderArchive
{
    private int archivedCount;

    public int ArchivedCount => this.archivedCount;

    // The method is async, so the lock of the object is released while it awaits.
    [Reentrant]
    public async Task ArchiveAsync( string order )
    {
        await Task.Delay( 10 );

        this.archivedCount++;
    }
}

public static class Program
{
    public static void Main()
    {
        var archive = new OrderArchive();
        archive.ArchiveAsync( "Order 1" ).Wait();
        archive.ArchiveAsync( "Order 2" ).Wait();
        Console.WriteLine( $"Archived orders: {archive.ArchivedCount}" );
    }
}
Archived orders: 2

See Also

Reference

ReentrantAttribute
SynchronizedAttribute
ActorAttribute

Other Resources

Synchronized Threading Model
Actor Threading Model