Skip to content

Conversation

@CyrusNajmabadi
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi commented Dec 15, 2021

Fixes AB#1413657

Seeing what breaks here.

@ghost ghost added the Area-IDE label Dec 15, 2021
await base.InitializeAsync(cancellationToken, progress).ConfigureAwait(true);

await TaskScheduler.Default;
_componentModel_doNotAccessDirectly = await this.GetServiceAsync<SComponentModel, IComponentModel>(throwOnFailure: true).ConfigureAwait(false);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jasonmalinowski do you see any issue doing things this way?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sharwell is the switch to the the BG necessary/desirable here? or would we just acquire from our async thread, adn allow that impl to be on the BG if it wanted to do that itself?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi Functionally it should be fine. My only "concern" would be whether we're able to instead do the async fetch closer to the actual thing that needs it. The downside generally putting things into package load is you're making everything that uses the package wait until this runs, even if the operation then didn't use it. Package load can be triggered for all sorts of random things, some of which may not use MEF and those are being slowed down. At some point we'll get asked why our package load is slow, we'll see this, and then get to sort out if we can remove this and push the work somewhere closer to its actual need.

Now getting off the soapbox, I admit it's not entirely clear to me what the "ideal" approach would be in this case. The editor factory needs the MEF container to actually create editor types. But we have to call RegisterEditorFactory and pass a concrete IVsEditorFactory instance (we can't pass an async lambda to produce it once it's needed), and the EditorFactory APIs (even IVsEditorFactory4) has no async entrypoints. So I'm happy to sign off on this, but would love it if @olegtk or somebody is able to chime in and explain how we're supposed to do this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually there might be one small alternative you could try would be to make that property be an async method and then thread through everything, but I suspect that'll touch a lot more than you want. But it'll make it much clearer what stuff is potentially being slowed down by fetching MEF.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will try that and see if it's managable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was definitely not managable. So many sync paths that would run into this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyrusNajmabadi Functionally it should be fine. My only "concern" would be whether we're able to instead do the async fetch closer to the actual thing that needs it. The downside generally putting things into package load is you're making everything that uses the package wait until this runs, even if the operation then didn't use it.

So here's the thing. InitializeAsync already directly needs the component model later due to:

            var miscellaneousFilesWorkspace = this.ComponentModel.GetService<MiscellaneousFilesWorkspace>();
            RegisterMiscellaneousFilesWorkspaceInformation(miscellaneousFilesWorkspace);

And one of the things we're solving is that the CreateEditorFactories call needs this as well:

        protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
        {
            await base.InitializeAsync(cancellationToken, progress).ConfigureAwait(true);

            await TaskScheduler.Default;
            _componentModel_doNotAccessDirectly = await this.GetServiceAsync<SComponentModel, IComponentModel>(throwOnFailure: true).ConfigureAwait(false);

            await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

            var shell = (IVsShell7)await GetServiceAsync(typeof(SVsShell)).ConfigureAwait(true);
            var solution = (IVsSolution)await GetServiceAsync(typeof(SVsSolution)).ConfigureAwait(true);
            cancellationToken.ThrowIfCancellationRequested();
            Assumes.Present(shell);
            Assumes.Present(solution);

            foreach (var editorFactory in CreateEditorFactories())

So all we're doing is ensuring we can get this component model in an non-blocking fashion prior to it being needed, vs having to immediately block the UI thread just a few instructions later.

Copy link
Contributor

@sharwell sharwell Dec 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sharwell is the switch to the the BG necessary/desirable here? or would we just acquire from our async thread, adn allow that impl to be on the BG if it wanted to do that itself?

The explicit switch to background thread is not desirable. GetServiceAsync will yield on its own if the requested value is not available. The expected pattern would be:

  1. Move this line after the explicit switch to main thread
  2. Use ConfigureAwait(true)

The end result will match what you see for IVsShell7 and IVsSolution a few lines below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do. on it now.

@CyrusNajmabadi CyrusNajmabadi marked this pull request as ready for review December 15, 2021 19:08
@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner December 15, 2021 19:08
Copy link
Member

@jasonmalinowski jasonmalinowski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be fine, ignoring the comments that this isn't "ideal"; but it's not clear to me how the ideal is even achieved in your scenario so this is better than nothing. That said there's some good room for followup here.

@jasonmalinowski
Copy link
Member

@CyrusNajmabadi This would also be a good place to run the internal VS integration tests prior to merging, since if this is going to break something, that might find it.

@CyrusNajmabadi
Copy link
Member Author

@CyrusNajmabadi This would also be a good place to run the internal VS integration tests prior to merging, since if this is going to break something, that might find it.

how do i do that? :)

@CyrusNajmabadi CyrusNajmabadi merged commit b246a00 into dotnet:main Dec 15, 2021
@CyrusNajmabadi CyrusNajmabadi deleted the componentModelBG branch December 15, 2021 21:09
@ghost ghost added this to the Next milestone Dec 15, 2021
@Cosifne Cosifne modified the milestones: Next, 17.1.P3 Jan 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants