Open sandboxFocusImprove this doc

LA0152: Awaiter Parameter Not Passed by Reference or of a Wrong Type

Severity Error
Reported by PostSharp
Message Incorrect type of the [Awaiter] parameter in "<Advice>". The parameter must be passed by reference and implement ICriticalNotifyCompletion or INotifyCompletion.

Cause

An entry or resume advice of an async method can make the method await an awaiter of the aspect before it continues: the advice assigns the awaiter to a parameter annotated with AwaiterAttribute and sets its [FlowBehavior] parameter to FlowBehavior.Yield. The advice gives the awaiter to the method through the parameter, so the parameter must be passed by reference, as out or ref, and its type must be an awaiter, which implements INotifyCompletion or ICriticalNotifyCompletion. PostSharp reports LA0152 when the parameter is passed by value or when its type implements neither interface.

In the following example, the [SwitchToThreadPool] aspect receives the awaiter by value.

using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0152_Error;

[PSerializable]
public class SwitchToThreadPoolAttribute : MethodLevelAspect
{
    [OnMethodEntryAdvice]
    public void OnEntry( [FlowBehavior] out FlowBehavior flowBehavior,
                         // The awaiter is passed by value, so the advice cannot give it to the method.
                         [Awaiter] YieldAwaitable.YieldAwaiter awaiter )
    {
        Console.WriteLine( "Switching to the thread pool." );
        flowBehavior = FlowBehavior.Yield;
    }
}

public class ReportService
{
    [SwitchToThreadPool]
    public async Task GenerateAsync()
    {
        Console.WriteLine( "Generating the report." );
        await Task.CompletedTask;
    }
}

public static class Program
{
    public static void Main()
    {
        new ReportService().GenerateAsync().Wait();
    }
}
error LA0152: LA0152_Error.cs(16,64): Incorrect type of the [Awaiter] parameter in "Errors.LA0152_Error.SwitchToThreadPoolAttribute.OnEntry(PostSharp.Aspects.FlowBehavior@, System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)". The parameter must be passed by reference and implement ICriticalNotifyCompletion or INotifyCompletion.

Resolution

  • Pass the [Awaiter] parameter as out or ref, and assign the awaiter in the advice.
  • Give the parameter the type of an awaiter, such as the one that Task.Yield().GetAwaiter() returns, or a type of your own that implements INotifyCompletion. A task is not an awaiter: use the awaiter that its GetAwaiter method returns.

In the following example, the advice assigns the awaiter to an out parameter, and the method continues on the thread pool.

using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Serialization;

namespace Errors.LA0152_Fixed;

[PSerializable]
public class SwitchToThreadPoolAttribute : MethodLevelAspect
{
    [OnMethodEntryAdvice]
    public void OnEntry( [FlowBehavior] out FlowBehavior flowBehavior,
                         // The awaiter is an out parameter, which the advice assigns.
                         [Awaiter] out YieldAwaitable.YieldAwaiter awaiter )
    {
        Console.WriteLine( "Switching to the thread pool." );
        awaiter = Task.Yield().GetAwaiter();
        flowBehavior = FlowBehavior.Yield;
    }
}

public class ReportService
{
    [SwitchToThreadPool]
    public async Task GenerateAsync()
    {
        Console.WriteLine( "Generating the report." );
        await Task.CompletedTask;
    }
}

public static class Program
{
    public static void Main()
    {
        new ReportService().GenerateAsync().Wait();
    }
}
Switching to the thread pool.
Generating the report.

See Also

Reference

AwaiterAttribute
FlowBehaviorAttribute
FlowBehavior

Other Resources

Advice Parameters