@samitouri / QOSAMI-WSL / commits / 5a34c2cd

Don't wait for the input relay when building an image (#40839)

* Don't wait for the input relay when building an image * Rethink logic * Apply PR feedback

Blue committed Jun 23, 2026 at 18:14 UTC 5a34c2cd70d8ab81215449799216ec54f0659561
3 files changed +41 -25
src/windows/common/HandleIO.cpp
+20 -19
@@ -1632,7 +1632,7 @@ MultiHandleWait& MultiHandleWait::operator=(MultiHandleWait&& other) noexcept
1632
1633 for (auto& entry : m_handles)
1634 {
1635 - entry->self = this;
1635 + entry->Self = this;
1636 }
1637
1638 // N.B. moving a MultiHandleWait() while running is not supported
@@ -1642,12 +1642,21 @@ MultiHandleWait& MultiHandleWait::operator=(MultiHandleWait&& other) noexcept
1642 return *this;
1643 }
1644
1645 -void MultiHandleWait::AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags)
1645 +void MultiHandleWait::AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags, OnError&& onError)
1646 {
1647 auto entry = std::make_unique<Entry>();
1648 entry->HandleFlags = flags;
1649 entry->Handle = std::move(handle);
1650 - entry->self = this;
1650 + entry->Self = this;
1651 +
1652 + if (WI_IsFlagSet(flags, Flags::IgnoreErrors))
1653 + {
1654 + entry->ErrorCallback = []() {};
1655 + }
1656 + else
1657 + {
1658 + entry->ErrorCallback = std::move(onError);
1659 + }
1660 m_handles.emplace_back(std::move(entry));
1661 }
1662
@@ -1660,8 +1669,8 @@ void NTAPI MultiHandleWait::WaitCallback(PVOID Context, BOOLEAN /*TimerOrWaitFir
1669 {
1670 auto* entry = static_cast<Entry*>(Context);
1671
1663 - entry->self->m_signaledHandles.push(entry);
1664 - entry->self->m_handleSignaledEvent.SetEvent();
1672 + entry->Self->m_signaledHandles.push(entry);
1673 + entry->Self->m_handleSignaledEvent.SetEvent();
1674 }
1675
1676 bool MultiHandleWait::Run(std::optional<std::chrono::milliseconds> Timeout)
@@ -1690,13 +1699,9 @@ bool MultiHandleWait::Run(std::optional<std::chrono::milliseconds> Timeout)
1699 }
1700 catch (...)
1701 {
1693 - if (WI_IsFlagSet(signaledEntry->HandleFlags, Flags::IgnoreErrors))
1694 - {
1695 - signaledEntry->Handle.reset();
1696 - continue;
1697 - }
1698 -
1699 - throw;
1702 + signaledEntry->ErrorCallback(); // Might throw and cancel the IO.
1703 + signaledEntry->Handle.reset();
1704 + continue;
1705 }
1706 }
1707
@@ -1715,13 +1720,9 @@ bool MultiHandleWait::Run(std::optional<std::chrono::milliseconds> Timeout)
1720 }
1721 catch (...)
1722 {
1718 - if (WI_IsFlagSet(entry.HandleFlags, Flags::IgnoreErrors))
1719 - {
1720 - entry.Handle.reset();
1721 - break;
1722 - }
1723 -
1724 - throw;
1723 + entry.ErrorCallback(); // Might throw and cancel the IO.
1724 + entry.Handle.reset();
1725 + break;
1726 }
1727 }
1728
src/windows/common/HandleIO.h
+5 -2
@@ -478,6 +478,8 @@ class MultiHandleWait
478 public:
479 NON_COPYABLE(MultiHandleWait);
480
481 + using OnError = std::function<void()>;
482 +
483 enum Flags
484 {
485 None = 0,
@@ -490,7 +492,7 @@ public:
492 MultiHandleWait(MultiHandleWait&&) noexcept;
493 MultiHandleWait& operator=(MultiHandleWait&&) noexcept;
494
493 - void AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags = Flags::None);
495 + void AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags = Flags::None, OnError&& onError = []() { throw; });
496 bool Run(std::optional<std::chrono::milliseconds> Timeout);
497 void Cancel();
498
@@ -499,7 +501,8 @@ private:
501 {
502 Flags HandleFlags{};
503 std::unique_ptr<OverlappedIOHandle> Handle;
502 - MultiHandleWait* self;
504 + MultiHandleWait* Self;
505 + OnError ErrorCallback;
506 };
507
508 static void NTAPI WaitCallback(PVOID Context, BOOLEAN TimerOrWaitFired);
src/windows/wslcsession/WSLCSession.cpp
+16 -4
@@ -955,8 +955,18 @@ try
955
956 auto io = CreateIOContext();
957
958 - io.AddHandle(std::make_unique<io::RelayHandle<io::ReadHandle>>(
959 - buildFileHandle.Get(), common::io::HandleWrapper{buildProcess.GetStdHandle(WSLCFDStdin)}));
958 + io.AddHandle(
959 + std::make_unique<io::RelayHandle<io::ReadHandle>>(buildFileHandle.Get(), common::io::HandleWrapper{buildProcess.GetStdHandle(WSLCFDStdin)}),
960 + MultiHandleWait::NeedNotComplete,
961 + [&buildProcess]() {
962 + // If we receive an error relaying stdin, it could be because the process exited.
963 + // Wait up to one second for the process to exit so errors in this relay don't override the actual build result.
964 + if (!buildProcess.GetExitEvent().wait(1000))
965 + {
966 + // Otherwise, throw the error and cancel the build.
967 + throw;
968 + }
969 + });
970
971 bool verbose = WI_IsFlagSet(Options->Flags, WSLCBuildImageFlagsVerbose);
972 std::string allOutput;
@@ -1350,8 +1360,10 @@ std::optional<std::string> WSLCSession::ImportImageImpl(DockerHTTPClient::HTTPRe
1360 LOG_LAST_ERROR_IF(shutdown(socket, SD_SEND) == SOCKET_ERROR);
1361 };
1362
1353 - io.AddHandle(std::make_unique<io::RelayHandle<io::ReadHandle>>(
1354 - common::io::HandleWrapper{userHandle.Get(), std::move(onInputComplete)}, common::io::HandleWrapper{Request.stream.native_handle()}));
1363 + io.AddHandle(
1364 + std::make_unique<io::RelayHandle<io::ReadHandle>>(
1365 + common::io::HandleWrapper{userHandle.Get(), std::move(onInputComplete)}, common::io::HandleWrapper{Request.stream.native_handle()}),
1366 + MultiHandleWait::NeedNotComplete);
1367
1368 io.AddHandle(std::make_unique<DockerHTTPClient::DockerHttpResponseHandle>(Request, std::move(onHttpResponse), std::move(onProgress)));
1369