-
Notifications
You must be signed in to change notification settings - Fork 196
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
Use a named pipe to communicate projectinfo in vscode #10521
Conversation
based approaches. The pipe will send updates that are serialized by messagepack and then deserialize and enqueue project updates. This should help improve project updates. There's also numerous logging added to help diagnose where issues might be coming from if users are hitting them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
...zor/src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorProjectInfoFactory.cs
Outdated
Show resolved
Hide resolved
...azor/src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorWorkspaceListener.cs
Outdated
Show resolved
Hide resolved
...azor/src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorWorkspaceListener.cs
Outdated
Show resolved
Hide resolved
return; | ||
} | ||
|
||
_logger.LogTrace("Opening named pipe server: {0}", pipeName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do all the processes write to a single log stream or does each one write to a different one? If different then strongly suggest you include the process id in any named pipe logging. Will save you hair pulling exercises in the future when trying to figure out which process is doing what 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In VS Code this goes to the vs code output window, so I think we'll be okay. We don't write to a file on disk :)
...azor/src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorWorkspaceListener.cs
Show resolved
Hide resolved
...azor/src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorWorkspaceListener.cs
Outdated
Show resolved
Hide resolved
...rosoft.AspNetCore.Razor.LanguageServer/ProjectSystem/NamedPipeBasedRazorProjectInfoDriver.cs
Outdated
Show resolved
Hide resolved
...rosoft.AspNetCore.Razor.LanguageServer/ProjectSystem/NamedPipeBasedRazorProjectInfoDriver.cs
Outdated
Show resolved
Hide resolved
...rosoft.AspNetCore.Razor.LanguageServer/ProjectSystem/NamedPipeBasedRazorProjectInfoDriver.cs
Outdated
Show resolved
Hide resolved
...rosoft.AspNetCore.Razor.LanguageServer/ProjectSystem/NamedPipeBasedRazorProjectInfoDriver.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.cs
Outdated
Show resolved
Hide resolved
…tSystem/NamedPipeBasedRazorProjectInfoDriver.cs Co-authored-by: Jared Parsons <[email protected]>
...zor/src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorProjectInfoFactory.cs
Outdated
Show resolved
Hide resolved
...rosoft.AspNetCore.Razor.LanguageServer/ProjectSystem/NamedPipeBasedRazorProjectInfoDriver.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.cs
Show resolved
Hide resolved
...azor/src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorWorkspaceListener.cs
Show resolved
Hide resolved
_stream = null; | ||
} | ||
|
||
public void NotifyDynamicFile(ProjectId projectId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this method be called from multiple threads?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what all methods in this type can be called from multiple threads?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NotifyDynamicFile
should be the only one. EnsureInitialized
is not thread safe and not intended to be. Workspace_WorkspaceChanged
is driven by workspace change events so ordered accordingly. UpdateCurrentProjectsAsync
is driven by the work queue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EnsureInitialized is protected by a lock: https://github.com/dotnet/roslyn/blob/602687a61e2ec18a1a2b4e498779a4037809c7e3/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorWorkspaceListenerInitializer.cs#L52
NotifyDynamicFile calls come from Roslyn's IDynamicFileInfoProvider, which guarantees non-overlapping calls I believe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mind if we talk offline for a bit? Might be quicker to share context and learn more about concerns
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What che(ck|que) do you think you're writing here that might be a problem?
Just wanted to validate my statement that calls to IDynamicFileInfoProvider.GetDynamicFileInfoAsync
won't overlap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean this one?
We absolutely will call that on multiple threads simultaneously.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad I checked!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAICT tell we should be okay still. This code hasn't changed in this PR, just moved. I can do another pass later to see if we can better clean this up, as well as better hook up the SolutionChanged
event that wasn't handled correctly before
...rosoft.AspNetCore.Razor.LanguageServer/ProjectSystem/NamedPipeBasedRazorProjectInfoDriver.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorConnectHandler.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorConnectHandler.cs
Outdated
Show resolved
Hide resolved
…tSystem/RazorConnectHandler.cs Co-authored-by: Jared Parsons <[email protected]>
.../src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorWorkspaceListenerBase.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I skimmed some of the Roslyn workspace interactions and nothing terrible jumped out.
_stream = null; | ||
} | ||
|
||
public void NotifyDynamicFile(ProjectId projectId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What che(ck|que) do you think you're writing here that might be a problem? Nothing jumps out as overly problematic.
|
||
// Schedule a task, in case adding a dynamic file is the last thing that happens | ||
_logger.LogTrace("{projectId} scheduling task due to dynamic file", projectId); | ||
_workQueue.AddWork(new UpdateWork(projectId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At first I was a bit worried what would happen if the project was removed while still handling this; I guess we'll see the removal after this event handler, and as long as the work queue doesn't reorder the remove operation then you're good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use the _workspace.CurrentSolution
and double check the project is still present. I believe that should suffice to make sure, and on the other end if we receive a remove that we never knew about we just ignore.
{ | ||
switch (e.Kind) | ||
{ | ||
case WorkspaceChangeKind.SolutionChanged: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SolutionChanged could also theoretically have projects removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm... we weren't handling that before. I'm guessing that was wrong. Do we need to do a diff of the ProjectIds?
case WorkspaceChangeKind.AnalyzerConfigDocumentRemoved: | ||
case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded: | ||
case WorkspaceChangeKind.AnalyzerConfigDocumentChanged: | ||
var projectId = e.ProjectId ?? e.DocumentId?.ProjectId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this just defensively written or are there cases where we might actually have a null ProjectId and a non-null DocumentId? I wouldn't think that was possible, but maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like I added it without much fanfare in #8607, so its possibly overly defensive, but this was written when we were having to be a bit secretive so if there was a real scenario, we didn't write it down anywhere.
src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/ProjectSystem/RazorConnectHandler.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.cs
Show resolved
Hide resolved
src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Protocol/RazorConnectParams.cs
Outdated
Show resolved
Hide resolved
|
||
private async Task ReadFromStreamAsync(CancellationToken cancellationToken = default) | ||
{ | ||
Logger?.LogTrace($"Starting read from named pipe."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In CreateNamedPipeAsync
Logger is not nullable, but here it could be? And also below this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interesting. I don't know if it's a bug or not, but if I remove all of the ?.
this warning goes away
.../src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorWorkspaceListenerBase.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy for any of these comments to be in a follow up if its easier, they're all nits/style related really.
.../src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorWorkspaceListenerBase.cs
Outdated
Show resolved
Hide resolved
.../src/Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace/RazorWorkspaceListenerBase.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.cs
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.cs
Outdated
Show resolved
Hide resolved
src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/StreamExtensions.cs
Outdated
Show resolved
Hide resolved
.../src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/AbstractRazorProjectInfoDriver.cs
Show resolved
Hide resolved
…Workspace/RazorWorkspaceListenerBase.cs Co-authored-by: David Wengier <[email protected]>
This changes from a file watcher approach to using named pipes. The flow for notifications is now:
This will require a dual insertion and VS Code changes.