Open sandboxFocusImprove this doc

LA0153: Awaiter Parameters of Different Types in One Advice Group

Severity Error
Reported by PostSharp
Message The advice "<Advice>" has an [Awaiter] parameter of type "<AwaiterType>", which is different from the type "<FirstAwaiterType>" of the [Awaiter] parameter of another advice of the same group.

Cause

The advices of an aspect that apply to the same target method form a group, and the woven method stores the awaiter of all their AwaiterAttribute parameters in a single variable. All the [Awaiter] parameters of a group must therefore have the same type. PostSharp compares each [Awaiter] parameter of the group with the first one, <FirstAwaiterType>, and reports LA0153 for each advice whose parameter has another type.

In the following example, the entry advice of the [SwitchToThreadPool] aspect has an awaiter of type YieldAwaitable.YieldAwaiter, and the resume advice an awaiter of type TaskAwaiter.

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

namespace Errors.LA0153_Error;

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

    [OnMethodResumeAdvice]
    public void OnResume( [FlowBehavior] out FlowBehavior flowBehavior,
                          // The awaiter of the resume advice has another type than the awaiter of the entry advice.
                          [Awaiter] out TaskAwaiter awaiter )
    {
        Console.WriteLine( "Resuming without switching." );
        awaiter = default;
        flowBehavior = FlowBehavior.Continue;
    }
}

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 LA0153: LA0153_Error.cs(25,53): The advice "Errors.LA0153_Error.SwitchToThreadPoolAttribute.OnResume(PostSharp.Aspects.FlowBehavior@, System.Runtime.CompilerServices.TaskAwaiter@)" has an [Awaiter] parameter of type "System.Runtime.CompilerServices.TaskAwaiter", which is different from the type "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" of the [Awaiter] parameter of another advice of the same group.

Resolution

  • Give the same awaiter type to all the [Awaiter] parameters of the group.
  • When the advices need different awaiters, write an awaiter type of your own that implements INotifyCompletion and wraps them, and use it in all the advices.

In the following example, both advices have an awaiter of type YieldAwaitable.YieldAwaiter.

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

namespace Errors.LA0153_Fixed;

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

    [OnMethodResumeAdvice]
    public void OnResume( [FlowBehavior] out FlowBehavior flowBehavior,
                          // Both advices have an awaiter of the same type.
                          [Awaiter] out YieldAwaitable.YieldAwaiter awaiter )
    {
        Console.WriteLine( "Resuming without switching." );
        awaiter = default;
        flowBehavior = FlowBehavior.Continue;
    }
}

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.
Resuming without switching.
Generating the report.

See Also

Reference

AwaiterAttribute
OnMethodEntryAdvice
OnMethodResumeAdvice

Other Resources

Advice Parameters
Advice Methods