Open sandboxFocusImprove this doc

LA0154: FlowBehavior.Yield Not Available Without an Awaiter Parameter

Severity Warning
Reported by PostSharp
Message Advice "<Advice>" cannot apply FlowBehavior.Yield to the async method "<Method>" because it does not have an [Awaiter] parameter.

Cause

In an async method, an OnMethodEntryAdvice or an OnMethodResumeAdvice can set its FlowBehaviorAttribute parameter to FlowBehavior.Yield, so that the method awaits an awaiter of the aspect before it continues. The awaiter comes from a parameter annotated with AwaiterAttribute. When the advice has a [FlowBehavior] parameter but no [Awaiter] parameter, PostSharp does not weave the code that yields, and reports LA0154 on the async method <Method>, once for each such advice.

PostSharp reports the warning whatever value the advice sets: also an advice that only uses FlowBehavior.Return, for instance to return a cached value, causes it.

In the following example, the entry advice of the [SwitchToThreadPool] aspect sets FlowBehavior.Yield but has no awaiter. The warning is reported on GenerateAsync, and the method does not yield: it runs on the thread of the caller.

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

namespace Errors.LA0154_Error;

[PSerializable]
public class SwitchToThreadPoolAttribute : MethodLevelAspect
{
    [OnMethodEntryAdvice]
    public void OnEntry( [FlowBehavior] out FlowBehavior flowBehavior )
    {
        flowBehavior = FlowBehavior.Yield;
    }
}

public class ReportService
{
    // The entry advice of the aspect sets FlowBehavior.Yield, but has no [Awaiter] parameter.
    [SwitchToThreadPool]
    public async Task GenerateAsync( int callerThreadId )
    {
        bool isCallerThread = Environment.CurrentManagedThreadId == callerThreadId;
        Console.WriteLine( $"Generating the report on the thread of the caller: {isCallerThread}." );
        await Task.CompletedTask;
    }
}

public static class Program
{
    public static void Main()
    {
        new ReportService().GenerateAsync( Environment.CurrentManagedThreadId ).Wait();
    }
}
warning LA0154: LA0154_Error.cs(23,23): Advice "Errors.LA0154_Error.SwitchToThreadPoolAttribute.OnEntry(PostSharp.Aspects.FlowBehavior@)" cannot apply FlowBehavior.Yield to the async method "Errors.LA0154_Error.ReportService.GenerateAsync(System.Int32)" because it does not have an [Awaiter] parameter.
Generating the report on the thread of the caller: True.

Resolution

  • When the advice must make the method yield, add an [Awaiter] parameter passed as out, and assign it an awaiter, such as the one that Task.Yield().GetAwaiter() returns.
  • When the advice never sets FlowBehavior.Yield, the warning does not affect the woven code, and you can ignore it.

In the following example, the entry advice assigns an awaiter, 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.LA0154_Fixed;

[PSerializable]
public class SwitchToThreadPoolAttribute : MethodLevelAspect
{
    [OnMethodEntryAdvice]
    public void OnEntry( [FlowBehavior] out FlowBehavior flowBehavior,
                         // The method awaits this awaiter, which continues on the thread pool.
                         [Awaiter] out YieldAwaitable.YieldAwaiter awaiter )
    {
        awaiter = Task.Yield().GetAwaiter();
        flowBehavior = FlowBehavior.Yield;
    }
}

public class ReportService
{
    [SwitchToThreadPool]
    public async Task GenerateAsync( int callerThreadId )
    {
        bool isCallerThread = Environment.CurrentManagedThreadId == callerThreadId;
        Console.WriteLine( $"Generating the report on the thread of the caller: {isCallerThread}." );
        await Task.CompletedTask;
    }
}

public static class Program
{
    public static void Main()
    {
        new ReportService().GenerateAsync( Environment.CurrentManagedThreadId ).Wait();
    }
}
Generating the report on the thread of the caller: False.

See Also

Reference

AwaiterAttribute
FlowBehaviorAttribute
FlowBehavior

Other Resources

Advice Parameters