Open sandboxFocusImprove this doc

LA0239: Required Current Task Not Available

Severity Error
Reported by PostSharp
Message The parameter "<Parameter>" of the advice "<Advice>" is bound by [CurrentTask] with IsRequired set to true, but the method "<TargetMethod>" cannot provide its task: only an async method that returns Task, Task<T>, ValueTask or ValueTask<T>, uses the default method builder, and is not implemented with runtime async can.

Cause

A parameter annotated with CurrentTaskAttribute receives the task that the caller of the async method observes. PostSharp reads it from the method builder of the state machine that the C# compiler generates. When IsRequired is true, the parameter must never be null, so PostSharp reports LA0239 on each target method that cannot provide its task:

  • A method that is not async, or an async void method, which has no task.
  • An async method whose method builder is not AsyncTaskMethodBuilder, AsyncTaskMethodBuilder<TResult>, AsyncValueTaskMethodBuilder or AsyncValueTaskMethodBuilder<TResult>, for instance a method annotated with [AsyncMethodBuilder( typeof( PoolingAsyncValueTaskMethodBuilder ) )], or a method that returns a custom task type.
  • An async method that the C# compiler implements with runtime async (<Features>runtime-async=on</Features>), which has no method builder. Without IsRequired, the parameter is null in such a method, and PostSharp reports the warning LA0240 instead.

PostSharp can also report LA0239 when it generates the code of the advice, when another transformation of the method prevents it from modifying the state machine.

In the following example, the [TrackTask] aspect is applied to an async void method.

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

namespace Errors.LA0239_Error;

[PSerializable]
public class TrackTaskAttribute : MethodLevelAspect
{
    [OnMethodResumeAdvice]
    public static void OnResume( [DeclarationName] string methodName, [CurrentTask( IsRequired = true )] Task task )
    {
        Console.WriteLine( $"{methodName} resumed, its task is completed: {task.IsCompleted}." );
    }
}

public class OrderService
{
    [TrackTask]
    public async Task SaveOrderAsync()
    {
        await Task.Delay( 10 );
        Console.WriteLine( "Order saved." );
    }

    // An async void method has no task that the caller observes.
    [TrackTask]
    public async void SendConfirmation()
    {
        await Task.Delay( 10 );
        Console.WriteLine( "Confirmation sent." );
    }
}

public static class Program
{
    public static async Task Main()
    {
        OrderService service = new OrderService();

        await service.SaveOrderAsync();
        service.SendConfirmation();

        await Task.Delay( 100 );
    }
}
error LA0239: LA0239_Error.cs(30,23): The parameter "task" of the advice "Errors.LA0239_Error.TrackTaskAttribute.OnResume(System.String, System.Threading.Tasks.Task)" is bound by [CurrentTask] with IsRequired set to true, but the method "Errors.LA0239_Error.OrderService.SendConfirmation" cannot provide its task: only an async method that returns Task, Task<T>, ValueTask or ValueTask<T>, uses the default method builder, and is not implemented with runtime async can.

Resolution

  • Make the method return a Task or a ValueTask, with the default method builder.
  • Or set IsRequired to false, and handle a null task in the advice.
  • Or apply the aspect only to methods that can provide their task. In a type-level aspect, a MatchPointcut skips the methods that cannot provide their task instead of reporting the error.

In the following example, the method returns a task.

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

namespace Errors.LA0239_Fixed;

[PSerializable]
public class TrackTaskAttribute : MethodLevelAspect
{
    [OnMethodResumeAdvice]
    public static void OnResume( [DeclarationName] string methodName, [CurrentTask( IsRequired = true )] Task task )
    {
        Console.WriteLine( $"{methodName} resumed, its task is completed: {task.IsCompleted}." );
    }
}

public class OrderService
{
    [TrackTask]
    public async Task SaveOrderAsync()
    {
        await Task.Delay( 10 );
        Console.WriteLine( "Order saved." );
    }

    // The method returns a task, which the caller can await.
    [TrackTask]
    public async Task SendConfirmationAsync()
    {
        await Task.Delay( 10 );
        Console.WriteLine( "Confirmation sent." );
    }
}

public static class Program
{
    public static async Task Main()
    {
        OrderService service = new OrderService();

        await service.SaveOrderAsync();
        await service.SendConfirmationAsync();
    }
}
SaveOrderAsync resumed, its task is completed: False.
Order saved.
SendConfirmationAsync resumed, its task is completed: False.
Confirmation sent.

See Also

Reference

CurrentTaskAttribute
OnMethodResumeAdvice

Other Resources

Advice Parameters
Semantic Advising of Iterator and Async Methods