Currently Microsoft.Build.Utilities.Task has a sync API of Execute() that needs to be implemented in derived class. What if the implementation has to invoke async methods, is there better pattern than blocking?
Execute() will have to remain synchronous, so you should just block.
"Why can't there be a new option for ExecuteAsync()?", you might ask. Because of overall MSBuild design decisions that can't be changed without a ton of effort.
Tasks have historically had full access to process state, including global state like environment variables and the current working directory. That implies that only a single task can execute concurrently in a process--otherwise you might get bad interleavings like
dir1.dir2.dir1.As a result, a process is completely committed to a task execution, and if a worker node is yielded (because a project needs the results of another project) we have a complex save/restore state process so that neither project has to attempt to preserve its own state.
Some tasks do depend on this, so it'd be a big effort to change it. And we still couldn't run multiple tasks concurrently, because the MSBuild execution model is that tasks are serialized within a target and targets are serialized within a project), so there's not much upside IMO.
If I were designing a build system today, it would not allow access to global state to cause this kind of problem and would probably have more async plumbing throughout. But we have to be compatible.
Most helpful comment
Execute()will have to remain synchronous, so you should just block."Why can't there be a new option for
ExecuteAsync()?", you might ask. Because of overall MSBuild design decisions that can't be changed without a ton of effort.Tasks have historically had full access to process state, including global state like environment variables and the current working directory. That implies that only a single task can execute concurrently in a process--otherwise you might get bad interleavings like
dir1.dir2.dir1.As a result, a process is completely committed to a task execution, and if a worker node is yielded (because a project needs the results of another project) we have a complex save/restore state process so that neither project has to attempt to preserve its own state.
Some tasks do depend on this, so it'd be a big effort to change it. And we still couldn't run multiple tasks concurrently, because the MSBuild execution model is that tasks are serialized within a target and targets are serialized within a project), so there's not much upside IMO.
If I were designing a build system today, it would not allow access to global state to cause this kind of problem and would probably have more async plumbing throughout. But we have to be compatible.