When using an async lambda inside Apply where we await asynchronous functions that don't return anything, the lambda gets the signature Func<T, Task>. Giving that to Apply the returned Task is not awaited because the overload chosen for the Apply is the one accepting Func<T, U> and this overload doesn't know how to await and register U where typeof(U) == typeof(Task)
Because we are not registering the task, it is not awaited properly and the Pulumi program runs to completion before the task is resolved.
We can easily repro this behavior as follows:
using System.Threading.Tasks;
using System;
using Pulumi;
await Deployment.RunAsync(() =>
{
Output.Create(0).Apply(async _ =>
{
await LongFailingTask();
});
});
async Task LongFailingTask()
{
Pulumi.Log.Info("LongFailingTask before waiting");
await Task.Delay(5000);
Pulumi.Log.Info("LongFailingTask after waiting");
throw new Exception("Boom!");
}
Running pulumi up on the code above will run the program but will never reach the exception after the delay:
Diagnostics:
pulumi:pulumi:Stack (project-dev):
LongFailingTask before waiting
The fix is to handle typeof(U) == typeof(Task) in a special way inside Apply where we create a wrapper task-returning function which awaits the task provided by the user and returns a dummy empty task so that the signature Func<T, Task> still holds
Fixes #139
When using an
async
lambda insideApply
where weawait
asynchronous functions that don't return anything, the lambda gets the signatureFunc<T, Task>
. Giving that toApply
the returnedTask
is not awaited because the overload chosen for theApply
is the one acceptingFunc<T, U>
and this overload doesn't know how to await and registerU
wheretypeof(U) == typeof(Task)
Because we are not registering the task, it is not awaited properly and the Pulumi program runs to completion before the task is resolved.
We can easily repro this behavior as follows:
Running
pulumi up
on the code above will run the program but will never reach the exception after the delay:The fix is to handle
typeof(U) == typeof(Task)
in a special way insideApply
where we create a wrapper task-returning function which awaits the task provided by the user and returns a dummy empty task so that the signatureFunc<T, Task>
still holds