Skip to content

Commit c1f9c09

Browse files
Fix deadlock if an MSBuild task is writing to stdout
When we switched over to communicating over a named pipe rather than stdin/stdout, we were still redirecting stdin and stdout. This had the side effect that if a build task was directly writing to standard out, the build would eventually deadlock since we weren't reading from the other side. I thought about simply not redirecting stdin/stdout, but I could imagine other problems might come up if we were to have multiple build hosts trying to share stdin/stdout. So now we'll log stdout the same way we log stderr, and explicitly close stdin so readers won't deadlock waiting for input. Fixes #78766
1 parent a1d33d1 commit c1f9c09

File tree

1 file changed

+9
-4
lines changed

1 file changed

+9
-4
lines changed

src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,8 @@ public BuildHostProcess(Process process, string pipeName, ILoggerFactory? logger
398398
_process.EnableRaisingEvents = true;
399399
_process.Exited += Process_Exited;
400400

401-
_process.ErrorDataReceived += Process_ErrorDataReceived;
401+
_process.OutputDataReceived += (_, e) => LogProcessOutput(e, "stdout");
402+
_process.ErrorDataReceived += (_, e) => LogProcessOutput(e, "stderr");
402403

403404
var pipeClient = NamedPipeUtil.CreateClient(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
404405
pipeClient.Connect(TimeOutMsNewProcess);
@@ -412,7 +413,11 @@ public BuildHostProcess(Process process, string pipeName, ILoggerFactory? logger
412413
_rpcClient.Disconnected += Process_Exited;
413414
BuildHost = new RemoteBuildHost(_rpcClient);
414415

415-
// Call this last so our type is fully constructed before we start firing events
416+
// Close the standard input stream so that if any build tasks were to try reading from the console, they won't deadlock waiting for input.
417+
_process.StandardInput.Close();
418+
419+
// Call Begin*ReadLine methods last so so our type is fully constructed before we start firing events.
420+
_process.BeginOutputReadLine();
416421
_process.BeginErrorReadLine();
417422
}
418423

@@ -421,14 +426,14 @@ private void Process_Exited(object? sender, EventArgs e)
421426
Disconnected?.Invoke(this, EventArgs.Empty);
422427
}
423428

424-
private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
429+
private void LogProcessOutput(DataReceivedEventArgs e, string outputName)
425430
{
426431
if (e.Data is not null)
427432
{
428433
lock (_processLogMessages)
429434
_processLogMessages.AppendLine(e.Data);
430435

431-
_logger?.LogTrace($"Message from Process: {e.Data}");
436+
_logger?.LogTrace($"Message on {outputName}: {e.Data}");
432437
}
433438
}
434439

0 commit comments

Comments
 (0)