diff --git a/src/Utilities.UnitTests/ToolTask_Tests.cs b/src/Utilities.UnitTests/ToolTask_Tests.cs
index 77d1a295e1f..9b36fb0eb5d 100644
--- a/src/Utilities.UnitTests/ToolTask_Tests.cs
+++ b/src/Utilities.UnitTests/ToolTask_Tests.cs
@@ -630,6 +630,8 @@ public void ToolTaskCanChangeCanonicalErrorFormat()
_output.WriteLine($"[ToolTaskCanChangeCanonicalErrorFormat] engine.Log:\n{engine.Log}");
}
+ _output.WriteLine(engine.Log);
+
// The above command logged a canonical warning, as well as a custom error.
engine.AssertLogContains("CS0168");
engine.AssertLogContains("The variable 'foo' is declared but never used");
@@ -1131,21 +1133,25 @@ public void ToolTaskDoesNotHangWhenGrandchildInheritsPipeHandles()
t.BuildEngine = engine;
// cmd echoes "hello", then starts a background ping that inherits
- // pipe handles. cmd exits immediately; ping outlives the 2s EOF timeout.
+ // pipe handles. cmd exits immediately; ping outlives the 30s EOF timeout.
t.MockCommandLineCommands = NativeMethodsShared.IsWindows
- ? "/c echo hello & start /b ping -n 10 127.0.0.1 > nul"
- : "-c \"echo hello; sleep 10 &\"";
+ ? "/c echo hello & start /b ping -n 40 127.0.0.1 > nul"
+ : "-c \"echo hello; sleep 40 &\"";
- // Set a generous timeout - without the fix this would hang for the full ping duration
- t.Timeout = 30000;
+ // Outer task timeout is generous; the EOF timeout (30s) is what bounds us.
+ t.Timeout = 60000;
+ var sw = Stopwatch.StartNew();
bool result = t.Execute();
+ sw.Stop();
- // The tool should complete without hanging.
- // The exit code may be non-zero depending on timing, but the key thing
- // is that Execute() returns at all rather than hanging forever.
_output.WriteLine(engine.Log);
+
engine.Log.ShouldContain("hello");
+ // The task must return within ~30s (EOF timeout) even though the grandchild lives longer.
+ sw.Elapsed.TotalSeconds.ShouldBeLessThan(35, "ToolTask should be bounded by the 30s EOF timeout, not the grandchild's lifetime");
+ // The diagnostic message must appear so CI reports show why the wait ended.
+ engine.Log.ShouldContain("Pipe EOF not received");
}
}
diff --git a/src/Utilities/Resources/Strings.resx b/src/Utilities/Resources/Strings.resx
index ad673518ba6..2167e4257b9 100644
--- a/src/Utilities/Resources/Strings.resx
+++ b/src/Utilities/Resources/Strings.resx
@@ -168,6 +168,9 @@
Environment Variables passed to tool:
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
MSB6011: Invalid parameters passed to the {0} task.{StrBegin="MSB6011: "}
diff --git a/src/Utilities/Resources/xlf/Strings.cs.xlf b/src/Utilities/Resources/xlf/Strings.cs.xlf
index 01efc804e00..d8db478a91b 100644
--- a/src/Utilities/Resources/xlf/Strings.cs.xlf
+++ b/src/Utilities/Resources/xlf/Strings.cs.xlf
@@ -72,6 +72,11 @@
Úlohu nelze přeskočit, protože není aktuální.
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: Příkaz {0} byl ukončen s kódem {1}.
diff --git a/src/Utilities/Resources/xlf/Strings.de.xlf b/src/Utilities/Resources/xlf/Strings.de.xlf
index f31ae54aa3e..7953fb83c84 100644
--- a/src/Utilities/Resources/xlf/Strings.de.xlf
+++ b/src/Utilities/Resources/xlf/Strings.de.xlf
@@ -72,6 +72,11 @@
Die Aufgabe kann nicht übersprungen werden, da sie nicht auf dem neuesten Stand ist.
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: "{0}" wurde mit dem Code {1} beendet.
diff --git a/src/Utilities/Resources/xlf/Strings.es.xlf b/src/Utilities/Resources/xlf/Strings.es.xlf
index 25a603833f9..404be9711e3 100644
--- a/src/Utilities/Resources/xlf/Strings.es.xlf
+++ b/src/Utilities/Resources/xlf/Strings.es.xlf
@@ -72,6 +72,11 @@
No se puede omitir la tarea porque no está actualizada.
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: "{0}" salió con el código {1}.
diff --git a/src/Utilities/Resources/xlf/Strings.fr.xlf b/src/Utilities/Resources/xlf/Strings.fr.xlf
index 9f7081d4b93..62aee2304ac 100644
--- a/src/Utilities/Resources/xlf/Strings.fr.xlf
+++ b/src/Utilities/Resources/xlf/Strings.fr.xlf
@@ -72,6 +72,11 @@
Nous n’avons pas pu ignorer la tâche, car elle n’est pas à jour.
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: Arrêt de "{0}" avec le code {1}.
diff --git a/src/Utilities/Resources/xlf/Strings.it.xlf b/src/Utilities/Resources/xlf/Strings.it.xlf
index b8571d61ca5..c36effec9b5 100644
--- a/src/Utilities/Resources/xlf/Strings.it.xlf
+++ b/src/Utilities/Resources/xlf/Strings.it.xlf
@@ -72,6 +72,11 @@
Non è possibile ignorare l'attività perché non è aggiornata.
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: "{0}" terminato con il codice {1}.
diff --git a/src/Utilities/Resources/xlf/Strings.ja.xlf b/src/Utilities/Resources/xlf/Strings.ja.xlf
index 32d07e722b9..390d2252e2e 100644
--- a/src/Utilities/Resources/xlf/Strings.ja.xlf
+++ b/src/Utilities/Resources/xlf/Strings.ja.xlf
@@ -72,6 +72,11 @@
タスクは最新ではないため、スキップできません。
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: "{0}" はコード {1} を伴って終了しました。
diff --git a/src/Utilities/Resources/xlf/Strings.ko.xlf b/src/Utilities/Resources/xlf/Strings.ko.xlf
index 92717cf6b5f..35d20b92f56 100644
--- a/src/Utilities/Resources/xlf/Strings.ko.xlf
+++ b/src/Utilities/Resources/xlf/Strings.ko.xlf
@@ -72,6 +72,11 @@
작업이 최신 상태가 아니므로 건너뛸 수 없습니다.
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: "{0}"이(가) 종료되었습니다(코드: {1}).
diff --git a/src/Utilities/Resources/xlf/Strings.pl.xlf b/src/Utilities/Resources/xlf/Strings.pl.xlf
index a9cd195e78e..e3a30fe7208 100644
--- a/src/Utilities/Resources/xlf/Strings.pl.xlf
+++ b/src/Utilities/Resources/xlf/Strings.pl.xlf
@@ -72,6 +72,11 @@
Nie można pominąć zadania, ponieważ nie jest ono aktualne.
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: Polecenie „{0}” zakończone przez kod {1}.
diff --git a/src/Utilities/Resources/xlf/Strings.pt-BR.xlf b/src/Utilities/Resources/xlf/Strings.pt-BR.xlf
index 1ad5f8e64c4..58b46449de2 100644
--- a/src/Utilities/Resources/xlf/Strings.pt-BR.xlf
+++ b/src/Utilities/Resources/xlf/Strings.pt-BR.xlf
@@ -72,6 +72,11 @@
Não foi possível ignorar a tarefa porque ela não está atualizada.
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: "{0}" foi encerrado com o código {1}.
diff --git a/src/Utilities/Resources/xlf/Strings.ru.xlf b/src/Utilities/Resources/xlf/Strings.ru.xlf
index 76e99c6ee79..a8f48b89954 100644
--- a/src/Utilities/Resources/xlf/Strings.ru.xlf
+++ b/src/Utilities/Resources/xlf/Strings.ru.xlf
@@ -72,6 +72,11 @@
Невозможно пропустить задачу, поскольку она не обновлена.
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: "{0}" завершилась с кодом {1}.
diff --git a/src/Utilities/Resources/xlf/Strings.tr.xlf b/src/Utilities/Resources/xlf/Strings.tr.xlf
index ad4d04c0dc3..2dbf03ad7df 100644
--- a/src/Utilities/Resources/xlf/Strings.tr.xlf
+++ b/src/Utilities/Resources/xlf/Strings.tr.xlf
@@ -72,6 +72,11 @@
Güncel olmadığı için görev atlanamıyor.
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: "{0}" öğesinden {1} koduyla çıkıldı.
diff --git a/src/Utilities/Resources/xlf/Strings.zh-Hans.xlf b/src/Utilities/Resources/xlf/Strings.zh-Hans.xlf
index 1513cad0038..fb427250d27 100644
--- a/src/Utilities/Resources/xlf/Strings.zh-Hans.xlf
+++ b/src/Utilities/Resources/xlf/Strings.zh-Hans.xlf
@@ -72,6 +72,11 @@
无法跳过任务,因为它不是最新的。
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: “{0}”已退出,代码为 {1}。
diff --git a/src/Utilities/Resources/xlf/Strings.zh-Hant.xlf b/src/Utilities/Resources/xlf/Strings.zh-Hant.xlf
index 9b40829dcf6..d2cae6d6368 100644
--- a/src/Utilities/Resources/xlf/Strings.zh-Hant.xlf
+++ b/src/Utilities/Resources/xlf/Strings.zh-Hant.xlf
@@ -72,6 +72,11 @@
無法略過工作,因為它不是最新的。
+
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+ Pipe EOF not received within {0} seconds. A grandchild process may still be holding the pipe open. Output already delivered has been logged.
+
+ MSB6006: "{0}" exited with code {1}.MSB6006: "{0}" 以返回碼 {1} 結束。
diff --git a/src/Utilities/ToolTask.cs b/src/Utilities/ToolTask.cs
index 624effa59af..e0c3037e441 100644
--- a/src/Utilities/ToolTask.cs
+++ b/src/Utilities/ToolTask.cs
@@ -1128,10 +1128,18 @@ private void WaitForProcessExit(Process proc)
//
// Use a bounded timeout as a safety net for the grandchild case where
// EOF never arrives because grand child inherited the pipe and keeps it open.
- const int eofTimeoutSec = 2;
+ const int eofTimeoutSec = 30;
WaitHandle[] eofEvents = [_standardOutputEOF, _standardErrorEOF];
- WaitHandle.WaitAll(eofEvents, TimeSpan.FromSeconds(eofTimeoutSec));
+ bool allEOFReceived = WaitHandle.WaitAll(eofEvents, TimeSpan.FromSeconds(eofTimeoutSec));
+ if (!allEOFReceived)
+ {
+ // Timeout: a grandchild process likely still holds the pipe open.
+ // Drain whatever data has already arrived before returning.
+ LogMessagesFromStandardError();
+ LogMessagesFromStandardOutput();
+ LogPrivate.LogMessageFromResources(MessageImportance.Low, "ToolTask.PipeEOFTimeout", eofTimeoutSec);
+ }
}
else
{