-
Notifications
You must be signed in to change notification settings - Fork 4.2k
fix for CLR_EXCEPTION_System.NullReferenceException_80004003_Microsoft.VisualStudio.InteractiveWindow.dll #26770
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
Conversation
|
retest this please #Resolved |
|
@tmat, @jinujoseph please review #Resolved |
|
@tmat, @jinujoseph please review. This is a regression fix we are going to insert into 15.7 servicing. #Resolved |
| } | ||
| } | ||
|
|
||
| await _host.TryGetOrCreateRemoteServiceAsync(processPendingOutput: true).ConfigureAwait(false); |
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.
await _host.TryGetOrCreateRemoteServiceAsync(processPendingOutput: true).ConfigureAwait(false); [](start = 20, length = 95)
Why is it ok to run this outside of the lock now? #Resolved
| { | ||
| ReportProcessExited(process); | ||
| return TryGetOrCreateRemoteServiceAsync(processPendingOutput: true); | ||
| } |
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.
Keep #Resolved
| _processExitHandlerStatus = ProcessExitHandlerStatus.Handled; | ||
| // Should set _processExitHandlerStatus before calling OnProcessExited to avoid deadlocks. | ||
| // Calling the host should be within the lock to prevent its disposing during the execution. | ||
| await _host.OnProcessExited(Process).ConfigureAwait(false); |
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.
await _host.OnProcessExited(Process).ConfigureAwait(false); [](start = 27, length = 60)
Move outside of the lock and keep. #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.
| } | ||
| } | ||
|
|
||
| internal void Dispose(bool joinThreads) |
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.
internal void Dispose(bool joinThreads) [](start = 11, length = 40)
Comment: may be called anytime #Resolved
before disposing remote service |
|
|
||
| lock(_errorOutputGuard) | ||
| { | ||
| _errorOutputGuard = TextWriter.Null; |
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.
_errorOutput = ... #Resolved
| private TextWriter _output; | ||
| private TextWriter _errorOutput; | ||
| private object _outputGuard; | ||
| private object _errorOutputGuard; |
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.
readonly for both. And do we need two lock objects or could the same object be used for both _output and _errorOutput? #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.
Thanks!
- Added readonly
- Discussed with Tomas to use a common lock or separate ones and found that separate ones should be better to avoid two channels to be executed independently.
In reply to: 188322002 [](ancestors = 188322002)
| var writer = error ? ErrorOutput : Output; | ||
| var writer = error ? _errorOutput : _output; | ||
| writer.Write(buffer, 0, count); | ||
| } |
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 we need to lock around writer.Write()? #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.
| lock (_errorOutputGuard) | ||
| { | ||
| _errorOutput.WriteLine(FeaturesResources.Failed_to_launch_0_process_exit_code_colon_1_with_output_colon, _hostPath, process.ExitCode); | ||
| _errorOutput.WriteLine(process.StandardError.ReadToEnd()); |
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.
process.StandardError.ReadToEnd() [](start = 43, length = 33)
Move process.StandardError.ReadToEnd() out of the lock. #Resolved
| } | ||
|
|
||
| public TextWriter Output | ||
| public void SetOutput(TextWriter value) |
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.
missing line break #Resolved
| // disposed or not reset: | ||
| Debug.Assert(currentRemoteService != null); | ||
| // Remote service may be disposed anytime. | ||
| if (currentRemoteService == null) { return default; } |
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.
{ [](start = 50, length = 1)
style: braces on separate lines #Resolved
| } | ||
| else | ||
| { | ||
| if (previousService == null) { return default; } |
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.
{ return default; } [](start = 53, length = 19)
style: braces on separate lines #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.
This needs to go after newService.Dispose(joinThreads: false);
Actually, even better: just move if (currentRemoteService == null) { return default; } as the first statement of the for loop and then this is not needed
In reply to: 188362651 [](ancestors = 188362651)
tmat
left a comment
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.
🕐
| oldOutput.Flush(); | ||
| } | ||
| var oldOutput = Interlocked.Exchange(ref _output, value); | ||
| oldOutput.Flush(); |
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.
Perhaps we should now do this:
lock (_outputGuard)
{
_output.Flush();
_output = value;
}
And then call SetOutput(TextWriter.Null); from Dispose #Resolved
tmat
left a comment
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.
![]()
…Microsoft.VisualStudio.InteractiveWindow.dll (dotnet#26770)" This reverts commit db46bcf.
…t.VisualStudio.InteractiveWindow.dll (dotnet#26770)
Customer scenario
Visual Studio closes or restarts and attempts to close and restart Roslyn which attempts to do the same with Interactive Host. Interactive Host attempts to send a message that it is closing back to Roslyn. However, Roslyn disappears just before. This is a race condition.
Bugs this fixes
https://devdiv.visualstudio.com/DevDiv/_workitems/edit/514822
Workarounds, if any
No
Risk
Low
Performance impact
None
Is this a regression from a previous update?
No
Root cause analysis
We haven't considered all possible timing combinations happening between threads.
How was the bug found?
Customer reports (Watson)