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

Use a named pipe to communicate projectinfo in vscode #10521

Merged
merged 34 commits into from
Jun 27, 2024

Conversation

ryzngard
Copy link
Contributor

This changes from a file watcher approach to using named pipes. The flow for notifications is now:

  1. Set up a handler that gets the name to connect to for a named pipe. This is generated as a GUID to avoid squatting. The Roslyn side will get this information through the razor/initialize message.
  2. Roslyn sets up the server, Razor sets up a client.
  3. All notifications go through that stream. MessagePack serialization is used for ProjectInfo still to serialize to bytes.

This will require a dual insertion and VS Code changes.

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.
@ryzngard ryzngard requested review from a team as code owners June 22, 2024 00:10
Copy link
Contributor

@davidwengier davidwengier left a comment

Choose a reason for hiding this comment

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

LGTM

return;
}

_logger.LogTrace("Opening named pipe server: {0}", pipeName);
Copy link
Member

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 😄

Copy link
Contributor Author

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 :)

_stream = null;
}

public void NotifyDynamicFile(ProjectId projectId)
Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes

Copy link
Member

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?

Copy link
Contributor Author

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

Copy link
Contributor

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.

Copy link
Contributor Author

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

Copy link
Contributor

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

Copy link
Member

@jasonmalinowski jasonmalinowski Jun 27, 2024

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Glad I checked!

Copy link
Contributor Author

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

…tSystem/RazorConnectHandler.cs

Co-authored-by: Jared Parsons <[email protected]>
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 skimmed some of the Roslyn workspace interactions and nothing terrible jumped out.

_stream = null;
}

public void NotifyDynamicFile(ProjectId projectId)
Copy link
Member

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));
Copy link
Member

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.

Copy link
Contributor Author

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:
Copy link
Member

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.

Copy link
Contributor Author

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;
Copy link
Member

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?

Copy link
Contributor

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/rzls/NamedPipeBasedRazorProjectInfoDriver.cs Outdated Show resolved Hide resolved

private async Task ReadFromStreamAsync(CancellationToken cancellationToken = default)
{
Logger?.LogTrace($"Starting read from named pipe.");
Copy link
Contributor

@davidwengier davidwengier Jun 27, 2024

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmm....

image

Copy link
Contributor Author

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/Razor/src/rzls/NamedPipeBasedRazorProjectInfoDriver.cs Outdated Show resolved Hide resolved
Copy link
Contributor

@davidwengier davidwengier left a 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.

@ryzngard ryzngard enabled auto-merge (squash) June 27, 2024 21:54
@ryzngard ryzngard merged commit 06ba187 into dotnet:main Jun 27, 2024
12 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Jun 27, 2024
@ryzngard ryzngard deleted the named_pipe branch June 27, 2024 22:44
@RikkiGibson RikkiGibson modified the milestones: Next, 17.12 Preview 1 Jul 29, 2024
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.

5 participants