Open sandboxFocusImprove this doc

THR017: Async Method Must Be Reentrant

Severity Error
Reported by PostSharp.Patterns.Threading
Message The async method "<Method>" must be annotated with the [Reentrant] custom attribute because the <ThreadingModel> threading model does not support non-reentrant async methods. A reentrant method releases the exclusivity guaranteed by the <ThreadingModel> model before awaiting and acquires it again when it resumes. Note that reacquiring a lock on resume makes the executing thread wait synchronously instead of awaiting the lock asynchronously.

Cause

An async method returns to its caller at its first await that does not complete immediately, and resumes later, often in another thread. A threading model that gives a thread exclusive access to the object cannot keep this access while the method awaits: it would hold a lock across threads, or block the queue of an actor. PostSharp supports only reentrant async methods, which release the exclusive access before each await and acquire it again when they resume. Because another thread can then access the object while the method awaits, you must confirm this behavior with the ReentrantAttribute custom attribute.

PostSharp reports THR017 for an async method without this attribute when:

<ThreadingModel> is the name of the threading model, for example Synchronized.

In the following example, the ProcessAsync method of the synchronized OrderProcessor class is async.

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

namespace Errors.THR017_Error;

[Synchronized]
public class OrderProcessor
{
    private int processedCount;

    public int ProcessedCount => this.processedCount;

    // The method awaits while it holds the lock of the object.
    public async Task ProcessAsync( string order )
    {
        await Task.Delay( 10 );
        this.processedCount++;
    }
}

public static class Program
{
    public static async Task Main()
    {
        var processor = new OrderProcessor();
        await processor.ProcessAsync( "Order 1" );
        await processor.ProcessAsync( "Order 2" );
        Console.WriteLine( $"Processed orders: {processor.ProcessedCount}" );
    }
}
error THR017: THR017_Error.cs(15,23): The async method "Errors.THR017_Error.OrderProcessor.ProcessAsync(System.String)" must be annotated with the [Reentrant] custom attribute because the Synchronized threading model does not support non-reentrant async methods. A reentrant method releases the exclusivity guaranteed by the Synchronized model before awaiting and acquires it again when it resumes. Note that reacquiring a lock on resume makes the executing thread wait synchronously instead of awaiting the lock asynchronously.

Resolution

  • Apply the ReentrantAttribute custom attribute to the method. Check that the method does not rely on the state of the object staying unchanged across an await: read the fields again after it.
  • When the method does not need exclusive access to the object, apply the ExplicitlySynchronizedAttribute custom attribute to it instead.

In the following example, ProcessAsync is reentrant.

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

namespace Errors.THR017_Fixed;

[Synchronized]
public class OrderProcessor
{
    private int processedCount;

    public int ProcessedCount => this.processedCount;

    // The method releases the lock before awaiting and acquires it again when it resumes.
    [Reentrant]
    public async Task ProcessAsync( string order )
    {
        await Task.Delay( 10 );
        this.processedCount++;
    }
}

public static class Program
{
    public static async Task Main()
    {
        var processor = new OrderProcessor();
        await processor.ProcessAsync( "Order 1" );
        await processor.ProcessAsync( "Order 2" );
        Console.WriteLine( $"Processed orders: {processor.ProcessedCount}" );
    }
}
Processed orders: 2

See Also

Reference

ReentrantAttribute
SynchronizedAttribute
ReaderWriterSynchronizedAttribute
ActorAttribute

Other Resources

Synchronized Threading Model
Reader/Writer Synchronized Threading Model
Actor Threading Model