Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose IVSGitExt as async VS service #1510

Merged

Conversation

@jcansdale
Copy link
Collaborator

@jcansdale jcansdale commented Feb 27, 2018

This PR does the following

  • Initializes PullRequestStatusBarPackage completely on a background thread
  • Expose IVSGitExt as an async VS service
  • Expose IVSGitExt as a MEF service
  • Initialize in background so as not to delay MEF loading
  • Extend IGitHubServiceProvider with GetServiceAsync<T>() and <T, Ret>
  • Use a delegate for GetServiceAsync
    • This avoids choosing one of the numerous and incompatible IAsyncServiceProvider implementations
  • Explicitly construct the correct implementation of IVSGitExt for the running DTE version
    • With the bonus of avoiding MEF errors for this service

It takes over from where #1506 left off, doing initialization asynchronously using GetServiceAsync to retrieve IGitExt. This means that we don't need to explicitly switch to the Main thread when retrieving IGitExt.

jcansdale added 8 commits Feb 23, 2018
Lock prevent tasks being executed at the same time but doesn't stop them being started out of order.
PendingTasks is used to execute tasks in order on the thread pool.
This will allow this service to be used by a package that is initializing on a background thread.
PendingTasks = InitializeAsync();
}

async Task InitializeAsync()

This comment has been minimized.

@jcansdale

jcansdale Feb 27, 2018
Author Collaborator

We could explicitly call this after constructing the service.

This comment has been minimized.

@jcansdale

jcansdale Feb 28, 2018
Author Collaborator

Since IVSGitExt is also consumed as a MEF service, we probably want the VS service to return as soon as possible (since there is no async loading for MEF services). Awaiting InitializeAsync before the service returns is probably a bad idea. Better to continue initializing in the background.

@@ -107,12 +112,28 @@ public GHClient(IProgram program)
}
}

[PartCreationPolicy(CreationPolicy.Shared)]
public class VSGitExtPart

This comment has been minimized.

@jcansdale

jcansdale Feb 28, 2018
Author Collaborator

I put this next to the export for IGitHubClient. Maybe we should collect all of the service exports together into a single file?

This comment has been minimized.

@shana

shana Feb 28, 2018
Collaborator

This shouldn't be here (and GitHubClient shouldn't be either so ignore that example :P). Ideally it should be right next to the implementation, but since we don't want to have multiple exports for both teamfoundation assemblies, and we don't have a separate assembly yet for mef implementations, this should probably go into GitHub.Exports or into the Services folder here in GitHub.VisualStudio.

It should also follow the convention of the other mef dispatchers and be called VSGitExtDispatcher. For it to actually work as a MEF dispatcher, it needs to implement IVSGitExt and have a mef export for that interface.


IVSGitExt CreateVSGitExt(IGitHubServiceProvider sp)
{
switch (VSVersion.Major)

This comment has been minimized.

@jcansdale

jcansdale Feb 28, 2018
Author Collaborator

In the past I've always used DTE.Version to differentiate versions of Visual Studio. This always returns a string with the major version number with a .0 (even for different minor versions). I used VSVersion because it was there, but I think it might hit the file system when reading the file version number.

@jcansdale jcansdale changed the title Expose IVSGitExt as async VS service [wip] Expose IVSGitExt as async VS service Feb 28, 2018
jcansdale added 2 commits Feb 28, 2018
Keep it consistent with GetService<T> and GetService<T, Ret>.
Fix failing VSGitExt unit tests.
@jcansdale jcansdale force-pushed the refactor/convert-VSGitExt-to-service branch from 81cc078 to 738b046 Feb 28, 2018
@jcansdale jcansdale requested a review from shana Feb 28, 2018
@jcansdale jcansdale changed the title [wip] Expose IVSGitExt as async VS service Expose IVSGitExt as async VS service Feb 28, 2018
Rather than extending IGitHubServiceProvider, pass a delegate for GetServiceAsync.
return new Lazy<IVSGitExt>(() => new VSGitExt14(GetServiceAsync)).Value;
case "15.0":
return new Lazy<IVSGitExt>(() => new VSGitExt15(GetServiceAsync)).Value;
default:

This comment has been minimized.

@shana

shana Feb 28, 2018
Collaborator

The logic to construct the correct type should be in the VSGitExtDispatcher, so callers don't have to worry about it.
Also, since it needs to call GetServiceAsync on an IAsyncServiceProvider, it should take IAsyncServiceProvider in its constructor, so we know that that's a dependency that this type has. Later, when GitHubServiceProvider supports async calls, we can implement IAsyncServiceProvider on it and pass it in without major changes to the code.

switch (dte.Version)
{
case "14.0":
return new Lazy<IVSGitExt>(() => new VSGitExt14(sp.GetServiceAsync)).Value;

This comment has been minimized.

@shana

shana Feb 28, 2018
Collaborator

Why not just pass the IAsyncServiceProvider into VSGitExt14/15 instead?
Also, any specific reason why you need a Lazy here? You're evaluating it immediately after creating, which feels like it defeats the purpose of the Lazy in the first place.

This comment has been minimized.

@jcansdale

jcansdale Feb 28, 2018
Author Collaborator

Why not just pass the IAsyncServiceProvider into VSGitExt14/15 instead?

The multiple implementations of IAsyncServiceProvider wreck havoc. The GitHub.TeamFoundation assemblies pick up more dependencies and I end up having to use extern alias. Passing GetServiceAsync as a delegate ends up being much simpler.

You're evaluating it immediately after creating, which feels like it defeats the purpose of the Lazy in the first place.

I've added some comments and change it to use Func<IVSGitExt> instead (using Lazy was a bit of a hack). By referencing VSGitExt14 and VSGitExt15 inside a lambda expression we can avoid loading the incompatible version into VS (e.g. we mustn't load VSGitExt15 into VS 2015).


public async static Task<IVSGitExt> Create(IAsyncServiceProvider sp)
{
var dte = await sp.GetServiceAsync(typeof(DTE)) as DTE;

This comment has been minimized.

@shana

shana Feb 28, 2018
Collaborator

ApplicationInfo.GetVersion will give you the running VS version as a proper type, so you don't have to grab a separate service for that here

jcansdale added 2 commits Feb 28, 2018
Cache the call to GetService<IVSGitExt>().
Use Func<IVSGitExt> rather than Lazy<IVSGitExt> (which was a hack).
@jcansdale jcansdale force-pushed the refactor/convert-VSGitExt-to-service branch from c78162b to fc06da7 Feb 28, 2018
- Use `UIContext.WhenActivated` instead of UIContext.UIContextChanged event
- Don't use async InitializeAsync now we're initializing on background thread
- No more need to use ContinueWith
@jcansdale jcansdale mentioned this pull request Mar 2, 2018
8 of 9 tasks complete
Simplify the VSGitExt service
@jcansdale jcansdale merged commit 32d2805 into feature/show-current-pr Mar 2, 2018
2 checks passed
2 checks passed
continuous-integration/appveyor/branch AppVeyor build succeeded
Details
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

2 participants
You can’t perform that action at this time.