From 24787e2ec631ca311e69e6fa8e43f6b7b3a73485 Mon Sep 17 00:00:00 2001 From: Nguyen Huu Linh Date: Thu, 14 May 2026 17:43:15 +0700 Subject: [PATCH 1/3] Fix ToolTask output loss: increase EOF pipe timeout from 2s to 30s --- src/Utilities/Resources/Strings.resx | 3 +++ src/Utilities/Resources/xlf/Strings.cs.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.de.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.es.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.fr.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.it.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.ja.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.ko.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.pl.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.pt-BR.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.ru.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.tr.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.zh-Hans.xlf | 5 +++++ src/Utilities/Resources/xlf/Strings.zh-Hant.xlf | 5 +++++ src/Utilities/ToolTask.cs | 12 ++++++++++-- 15 files changed, 78 insertions(+), 2 deletions(-) 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 05ed6aca646..3220228468a 100644 --- a/src/Utilities/ToolTask.cs +++ b/src/Utilities/ToolTask.cs @@ -1129,10 +1129,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 { From d00d4b3253a8da91c245c004b31fc1cecc50d300 Mon Sep 17 00:00:00 2001 From: Nguyen Huu Linh Date: Mon, 18 May 2026 16:25:46 +0700 Subject: [PATCH 2/3] Update --- src/Utilities.UnitTests/ToolTask_Tests.cs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Utilities.UnitTests/ToolTask_Tests.cs b/src/Utilities.UnitTests/ToolTask_Tests.cs index b937b7b829e..61a1f260e1f 100644 --- a/src/Utilities.UnitTests/ToolTask_Tests.cs +++ b/src/Utilities.UnitTests/ToolTask_Tests.cs @@ -590,6 +590,8 @@ public void ToolTaskCanChangeCanonicalErrorFormat() t.Execute(); + _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"); @@ -1063,21 +1065,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 60 &\""; - // 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"); } } From 360a6206cbf702b1329d4d00154bb9887bab2ce4 Mon Sep 17 00:00:00 2001 From: Nguyen Huu Linh Date: Wed, 20 May 2026 14:02:52 +0700 Subject: [PATCH 3/3] Fixed sleep time to 40 --- src/Utilities.UnitTests/ToolTask_Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utilities.UnitTests/ToolTask_Tests.cs b/src/Utilities.UnitTests/ToolTask_Tests.cs index 61a1f260e1f..4be0cffed84 100644 --- a/src/Utilities.UnitTests/ToolTask_Tests.cs +++ b/src/Utilities.UnitTests/ToolTask_Tests.cs @@ -1068,7 +1068,7 @@ public void ToolTaskDoesNotHangWhenGrandchildInheritsPipeHandles() // pipe handles. cmd exits immediately; ping outlives the 30s EOF timeout. t.MockCommandLineCommands = NativeMethodsShared.IsWindows ? "/c echo hello & start /b ping -n 40 127.0.0.1 > nul" - : "-c \"echo hello; sleep 60 &\""; + : "-c \"echo hello; sleep 40 &\""; // Outer task timeout is generous; the EOF timeout (30s) is what bounds us. t.Timeout = 60000;