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

[WIP] Implement TempPEBuildManager using UnconfiguredProjectHostBridge #4318

Closed
wants to merge 18 commits into from

Conversation

davidwengier
Copy link
Member

There is still lots to be done in this, and most of it not documented in comments, but just wanted to get a quick look over the work so far to make sure its on the right track. The main thing is the TempPEManager and its implementation of UnconfiguredProjectHostBridge.

The idea goes like this:

  • TempPEBuildManager receives updates to source items view PreprocessAsync
  • That method pulls out the changes relevant to us (TODO: ensure ShouldApplyChanges returns false when there are no design time changes)
  • Those changes are then pushed to the UI thread in ApplyAsync, including creating a CancellationSeries for each file
  • In GetTempPEBlob we utilise the CancellationSeries (thanks Drew) in order to cancel any in-progress compiles for the input file, so the new one is the only one.
    • The theory here is that we can avoid the need for the event queue that legacy has since our builds are cancellable

Big TODO items are:

  • Events
  • Taking a dependency on new Roslyn bits (commented out code is correct though)
  • Checking if we actually need to compile based on file datetime
    • This is arguably unnecessary with project changes being pushed to us, but loading a project could benefit from it
  • Listening to events (if applicable?)

Have at it. I expect lots of comments :P

@davidwengier davidwengier requested review from drewnoakes, davkean and a team November 26, 2018 06:00
@jmarolf
Copy link
Contributor

jmarolf commented Nov 28, 2018

@davidwengier will this every be called from background threads / multiple threads at once?

private IActiveConfiguredProjectSubscriptionService ProjectSubscriptionService { get; set; }

[ProjectAutoLoad]
[AppliesTo(ProjectCapability.CSharpOrVisualBasic)]
Copy link
Member

Choose a reason for hiding this comment

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

This needs to be CSharpOrVisualBasicLanguageService.

Copy link
Member Author

Choose a reason for hiding this comment

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

That doesn't exist. Do you mean DotNetLanguageService?

Copy link
Member

Choose a reason for hiding this comment

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

Depends, what happens when you call into Roslyn and the language is F#?

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 don't know. Do we want TempPE for F#? Whats the difference between CSharpOrVisualBasic and CSharpOrVisualBasicLanguageService?

@davidwengier
Copy link
Member Author

The PreprocessAsync and ApplyAsync methods are called from background threads, though not multiple threads at once to the same instance as far as I know. The ITempPEBuildManager members are both UI thread I think, but again not multiple threads at once. Why do you ask?

@davkean
Copy link
Member

davkean commented Nov 28, 2018

There is nothing stopping DisposeAsync from being called underneath while either the UI thread is doing something, or while you are processing changes in reaction to data flow changes.

/// Manages the portable executable (PE) files produced by running custom tools.
/// </summary>
[Export(typeof(BuildManager))]
[AppliesTo(ProjectCapability.CSharpOrVisualBasic)]
Copy link
Member

@davkean davkean Nov 28, 2018

Choose a reason for hiding this comment

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

CSharpOrVisualBasicLanguageService

@jmarolf
Copy link
Contributor

jmarolf commented Nov 28, 2018

Why do you ask?

Just trying to reason about the logic. My understanding is that

  1. We only want one compilation to be in-progress at any given point
  2. We want calls to GetTempPEBlobAsync to cancel active compilations and queue a new one

What you have seems correct. Are callers to GetTempPEBlobAsync expected to handle operation cancelled exceptions and re-queue?

}, value.DataSourceVersions);

// Fire off the events
var buildManager = BuildManager.First().Value as VSBuildManager;
Copy link
Member

Choose a reason for hiding this comment

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

We have a hard dependency on the BuildManager being; BuildManagerEvents and VSBuildManager. Please just import that.

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you expand on that? Do you mean directly importing VSBuildManager (need to add another Export attribute to it?) or do you mean importing VSProjectEvents and access through BuildManagerEvents so that we're ensuring our dependency is hooked up correctly?

@davkean
Copy link
Member

davkean commented Nov 28, 2018

Cancellation is a little unclear to me, how does this work? Looks like we're compiling on the UI thread in response to someone asking on the UI thread blocking us from presumably updating the AppliedValue, how exactly does cancellation work?

@davidwengier
Copy link
Member Author

The cancellation stuff is still just a theory, but you raise a good point. The cancellation would need to happen in PreprocessAsync or ApplyAsync but off the UI thread. The theory is simply to avoid needing the event loop that legacy has, we can simply cancel any current compilation and let the next one happen as change flow through to the UI thread. Now that I'm writing that out though, perhaps thats not necessary at all, and the fact that compilation is happening on the UI thread will be enough - we can just let the current compilations finish and then the next set of changes will push through anyway.

@davidwengier
Copy link
Member Author

Are callers to GetTempPEBlobAsync expected to handle operation cancelled exceptions and re-queue?

No, the theory is that the thing doing the cancellation is doing so because its in the process of re-queuing.

@jmarolf
Copy link
Contributor

jmarolf commented Nov 28, 2018

For my own edification, why does compilation need to be on the UI thread?

@davkean
Copy link
Member

davkean commented Nov 28, 2018

The compilation is in response to a call from the UI thread. The UI thread is blocked on the call, nothing else will execute on the UI thread (unless it unblocks the call) until the call is finished. There's probably no benefit to it running on a background thread.

However, perhaps we could reconsider the design where there is a benefit? I can't remember what legacy does here, do you remember?

@davidwengier
Copy link
Member Author

Legacy uses a background thread.

@davkean
Copy link
Member

davkean commented Nov 28, 2018

Can you expand on that? What benefit is it hoping from using a background thread?

@davidwengier
Copy link
Member Author

I don't know why it is using a background thread, there aren't any comments indicating any reasons in the source. I thought we wanted to compile on the UI thread because that way we're guaranteed that the project state is consistent because we're not passing all of the info to Roslyn any more, but relying on the fact that AppliedValue and roslyns CurrentSolution are both only changed on the UI thread.

@davkean
Copy link
Member

davkean commented Nov 28, 2018

UI thread is blocked, so CurrentSolution/AppliedValue cannot change regardless of where you compile. Let me try and understand why/what it is doing.

@davidwengier
Copy link
Member Author

I don't claim to be able to read the C++ code well, but in theory the UI thread doesn't have to be blocked on the compile, just the returning of the XML string. That can be done using only file names.

Why you would want to tell someone a DLL is somewhere, then schedule a task to make it appear there at some point in time in the future, I certainly don't know.

@davidwengier
Copy link
Member Author

I've pushed a bunch of changes including removing the CancellationSeries and adding in support for firing the dirty event when files change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants