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