-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Loading PR list blocks await JoinableTaskFactory.SwitchToMainThreadAsync #1537
Description
How to repro
- Install the following repro
AsyncPackageRepro.zip
[Guid("aae76547-10c6-4a2d-b33a-76ded02ac07b")]
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[ProvideAutoLoad(Constants.vsContextNoSolution, PackageAutoLoadFlags.BackgroundLoad)]
public sealed class MyAsyncPackage : AsyncPackage
{
protected override async Task InitializeAsync(
CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
{
Trace.WriteLine("Executed when VS opens without a solution");
await JoinableTaskFactory.SwitchToMainThreadAsync();
Trace.WriteLine("Executed when PR list on GitHub pane has finished loading");
}
}- Open Visual Studio 2015 with the
GitHubpane visible and the PR list loading - The
await JoinableTaskFactory.SwitchToMainThreadAsync()line only returns once the PR list has completely finished refreshing (which can take a long time)
What is impacted
- This is a problem when installing the PR status bar UI (which must be done on the Main thread). The current PR sometimes don't appear for a long time.
- This will also be a problem for packages that auto-load in order to handle command visibility (e.g.
GitHubPackageandInlineReviewsPackage). - Any other extensions that use
SwitchToMainThreadAsyncwhen they auto-load
Notes
This hasn't been a problem in the past because we haven't been using the AllowsBackgroundLoading = true and PackageAutoLoadFlags.BackgroundLoad options. Developers are now being strongly encouraged to enable these options. We will potentially be delaying when other extensions wake up.
This isn't necessarily a problem when a command is executed because in this case InitializeAsync starts off on the Main thread and SwitchToMainThreadAsync is a nop. If however auto-load was started before the command is executed, it will likely still get blocked.
How to fix
Update: This appears to be the best solution:
protected override async Task InitializeAsync()
{
// When SwitchToMainThreadAsync is called, use Normal priority (not Background)
await JoinableTaskFactory.RunAsync(VsTaskRunContext.UIThreadNormalPriority, InitializeMenus);
}
async Task InitializeMenus()
{
// This doesn't require the Main thread
var menuService = (IMenuCommandService)(await GetServiceAsync(typeof(IMenuCommandService)));
await JoinableTaskFactory.SwitchToMainThreadAsync();
// This does require the Main thread
menuService.AddCommands(...);
}This can be resolved from inside InitializeAsync by using the following:
Unfortunately the following doesn't seem to work. SwitchToMainThreadAsync still doesn't return until after the GitHub pane has finished refreshing (this can take a while).
// `JoinableTaskFactory` is a property on `AsyncPackage`
await JoinableTaskFactory
.WithPriority(VsTaskRunContext.UIThreadNormalPriority)
.SwitchToMainThreadAsync();I've tried the following as a workaround:
public static Task RunOnMainThreadNormalPriority(Action action)
{
var service = (IVsTaskSchedulerService2)VsTaskLibraryHelper.ServiceInstance;
var scheduler = service.GetTaskScheduler((uint)VsTaskRunContext.UIThreadNormalPriority);
return Task.Factory.StartNew(action, default(CancellationToken),
TaskCreationOptions.HideScheduler, scheduler);
}This seems to work in simple cases, but if the executed code calls GetService it will deadlock (I think that's what's happening). We still need to find a solution.
We should also consider refreshing the PR list at a lower priority in order not to block other extensions.
Related
- Fix deadlock when VS is opended with GitHub pane visible: Fix deadlock when VS is opended with GitHub pane visible #1536
- Thanks @sharwell for his help on Gitter! https://gitter.im/Microsoft/extendvs?at=5aaa0ccd35dd17022e5f62a3