fix for CLR_EXCEPTION_System.NullReferenceException_80004003_Microsoft.VisualStudio.InteractiveWindow.dll#26770
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.
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); | ||
| } |
| _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.
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.
| @@ -117,16 +119,13 @@ private void ReadOutput(bool error) | |||
| internal void Dispose(bool joinThreads) | |||
There was a problem hiding this comment.
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.
_errorOutput = ... #Resolved
| private TextWriter _output; | ||
| private TextWriter _errorOutput; | ||
| private object _outputGuard; | ||
| private object _errorOutputGuard; |
There was a problem hiding this comment.
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.
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.
Do we need to lock around writer.Write()? #Resolved
There was a problem hiding this comment.
| 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.
process.StandardError.ReadToEnd() [](start = 43, length = 33)
Move process.StandardError.ReadToEnd() out of the lock. #Resolved
| } | ||
|
|
||
| public TextWriter Output | ||
| public void SetOutput(TextWriter value) |
| // 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.
{ [](start = 50, length = 1)
style: braces on separate lines #Resolved
| } | ||
| else | ||
| { | ||
| if (previousService == null) { return default; } |
There was a problem hiding this comment.
{ return default; } [](start = 53, length = 19)
style: braces on separate lines #Resolved
There was a problem hiding this comment.
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)
| oldOutput.Flush(); | ||
| } | ||
| var oldOutput = Interlocked.Exchange(ref _output, value); | ||
| oldOutput.Flush(); |
There was a problem hiding this comment.
Perhaps we should now do this:
lock (_outputGuard)
{
_output.Flush();
_output = value;
}
And then call SetOutput(TextWriter.Null); from Dispose #Resolved
…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)