Rethink the Accept() logic to differentiate between errors and cancellation (#14156)

* Bring relay changes * Redesign Accept() logic to differenciate between cancellation and errors * Prepare for PR * Apply PR feedback

Blue committed Feb 4, 2026 at 19:02 UTC 1897d055ddf6afa05f148c0a5b80090e7b55ac3c
10 files changed +439 -35
src/shared/inc/defs.h
+12
@@ -22,6 +22,18 @@ Abstract:
22 #define _wcsicmp wcscasecmp
23 #endif
24
25 +#define NON_COPYABLE(Type) \
26 + Type(const Type&) = delete; \
27 + Type& operator=(const Type&) = delete;
28 +
29 +#define NON_MOVABLE(Type) \
30 + Type(Type&&) = delete; \
31 + Type& operator=(Type&&) = delete;
32 +
33 +#define DEFAULT_MOVABLE(Type) \
34 + Type(Type&&) = default; \
35 + Type& operator=(Type&&) = default;
36 +
37 namespace wsl::shared {
38
39 inline constexpr std::uint32_t VersionMajor = WSL_PACKAGE_VERSION_MAJOR;
src/windows/common/hvsocket.cpp
+6 -3
@@ -39,11 +39,14 @@ void InitializeWildcardSocketAddress(_Out_ PSOCKADDR_HV Address)
39 }
40 } // namespace
41
42 -wil::unique_socket wsl::windows::common::hvsocket::Accept(
43 - _In_ SOCKET ListenSocket, _In_ int Timeout, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)
42 +std::optional<wil::unique_socket> wsl::windows::common::hvsocket::CancellableAccept(
43 + _In_ SOCKET ListenSocket, _In_ DWORD Timeout, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)
44 {
45 wil::unique_socket Socket = Create();
46 - wsl::windows::common::socket::Accept(ListenSocket, Socket.get(), Timeout, ExitHandle, Location);
46 + if (!socket::CancellableAccept(ListenSocket, Socket.get(), Timeout, ExitHandle, Location))
47 + {
48 + return {};
49 + }
50
51 return Socket;
52 }
src/windows/common/hvsocket.hpp
+2 -2
@@ -19,9 +19,9 @@ Abstract:
19
20 namespace wsl::windows::common::hvsocket {
21
22 -wil::unique_socket Accept(
22 +std::optional<wil::unique_socket> CancellableAccept(
23 _In_ SOCKET ListenSocket,
24 - _In_ int Timeout,
24 + _In_ DWORD Timeout,
25 _In_opt_ HANDLE ExitHandle = nullptr,
26 const std::source_location& Location = std::source_location::current());
27
src/windows/common/relay.cpp
+219 -2
@@ -16,8 +16,14 @@ Abstract:
16 #include "relay.hpp"
17 #pragma hdrstop
18
19 +using wsl::windows::common::relay::EventHandle;
20 +using wsl::windows::common::relay::HandleWrapper;
21 +using wsl::windows::common::relay::IOHandleStatus;
22 +using wsl::windows::common::relay::MultiHandleWait;
23 +using wsl::windows::common::relay::OverlappedIOHandle;
24 using wsl::windows::common::relay::ScopedMultiRelay;
25 using wsl::windows::common::relay::ScopedRelay;
26 +using wsl::windows::common::relay::SingleAcceptHandle;
27
28 namespace {
29
@@ -108,7 +114,7 @@ wsl::windows::common::relay::InterruptableRead(
114 return 0;
115 }
116
111 - THROW_LAST_ERROR_IF(lastError != ERROR_IO_PENDING);
117 + THROW_LAST_ERROR_IF_MSG(lastError != ERROR_IO_PENDING, "Handle: 0x%p", (void*)InputHandle);
118
119 auto cancelRead = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {
120 CancelIoEx(InputHandle, Overlapped);
@@ -569,4 +575,215 @@ try
575 }
576 }
577 }
572 -CATCH_LOG()
\ No newline at end of file
578 +CATCH_LOG()
579 +
580 +void MultiHandleWait::AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags)
581 +{
582 + m_handles.emplace_back(flags, std::move(handle));
583 +}
584 +
585 +void MultiHandleWait::Cancel()
586 +{
587 + m_cancel = true;
588 +}
589 +bool MultiHandleWait::Run(std::optional<std::chrono::milliseconds> Timeout)
590 +{
591 + m_cancel = false; // Run may be called multiple times.
592 +
593 + std::optional<std::chrono::steady_clock::time_point> deadline;
594 +
595 + if (Timeout.has_value())
596 + {
597 + deadline = std::chrono::steady_clock::now() + Timeout.value();
598 + }
599 +
600 + // Run until all handles are completed.
601 +
602 + while (!m_handles.empty() && !m_cancel)
603 + {
604 + // Schedule IO on each handle until all are either pending, or completed.
605 + for (size_t i = 0; i < m_handles.size(); i++)
606 + {
607 + while (m_handles[i].second->GetState() == IOHandleStatus::Standby)
608 + {
609 + try
610 + {
611 + m_handles[i].second->Schedule();
612 + }
613 + catch (...)
614 + {
615 + if (WI_IsFlagSet(m_handles[i].first, Flags::IgnoreErrors))
616 + {
617 + m_handles[i].second.reset(); // Reset the handle so it can be deleted.
618 + }
619 + else
620 + {
621 + throw;
622 + }
623 + }
624 + }
625 + }
626 +
627 + // Remove completed handles from m_handles.
628 + for (auto it = m_handles.begin(); it != m_handles.end();)
629 + {
630 + if (!it->second)
631 + {
632 + it = m_handles.erase(it);
633 + }
634 + else if (it->second->GetState() == IOHandleStatus::Completed)
635 + {
636 + if (WI_IsFlagSet(it->first, Flags::CancelOnCompleted))
637 + {
638 + m_cancel = true; // Cancel the IO if a handle with CancelOnCompleted is in the completed state.
639 + }
640 +
641 + it = m_handles.erase(it);
642 + }
643 + else
644 + {
645 + ++it;
646 + }
647 + }
648 +
649 + if (m_handles.empty() || m_cancel)
650 + {
651 + break;
652 + }
653 +
654 + // Wait for the next operation to complete.
655 + std::vector<HANDLE> waitHandles;
656 + for (const auto& e : m_handles)
657 + {
658 + waitHandles.emplace_back(e.second->GetHandle());
659 + }
660 +
661 + DWORD waitTimeout = INFINITE;
662 + if (deadline.has_value())
663 + {
664 + auto miliseconds =
665 + std::chrono::duration_cast<std::chrono::milliseconds>(deadline.value() - std::chrono::steady_clock::now()).count();
666 +
667 + waitTimeout = static_cast<DWORD>(std::max(0LL, miliseconds));
668 + }
669 +
670 + auto result = WaitForMultipleObjects(static_cast<DWORD>(waitHandles.size()), waitHandles.data(), false, waitTimeout);
671 + if (result == WAIT_TIMEOUT)
672 + {
673 + THROW_WIN32(ERROR_TIMEOUT);
674 + }
675 + else if (result >= WAIT_OBJECT_0 && result < WAIT_OBJECT_0 + m_handles.size())
676 + {
677 + auto index = result - WAIT_OBJECT_0;
678 +
679 + try
680 + {
681 + m_handles[index].second->Collect();
682 + }
683 + catch (...)
684 + {
685 + if (WI_IsFlagSet(m_handles[index].first, Flags::IgnoreErrors))
686 + {
687 + m_handles.erase(m_handles.begin() + index);
688 + }
689 + else
690 + {
691 + throw;
692 + }
693 + }
694 + }
695 + else
696 + {
697 + THROW_LAST_ERROR_MSG("Timeout: %lu, Count: %llu", waitTimeout, waitHandles.size());
698 + }
699 + }
700 +
701 + return !m_cancel;
702 +}
703 +
704 +IOHandleStatus OverlappedIOHandle::GetState() const
705 +{
706 + return State;
707 +}
708 +
709 +EventHandle::EventHandle(HandleWrapper&& Handle, std::function<void()>&& OnSignalled) :
710 + Handle(std::move(Handle)), OnSignalled(std::move(OnSignalled))
711 +{
712 +}
713 +
714 +void EventHandle::Schedule()
715 +{
716 + State = IOHandleStatus::Pending;
717 +}
718 +
719 +void EventHandle::Collect()
720 +{
721 + State = IOHandleStatus::Completed;
722 + OnSignalled();
723 +}
724 +
725 +HANDLE EventHandle::GetHandle() const
726 +{
727 + return Handle.Get();
728 +}
729 +
730 +SingleAcceptHandle::SingleAcceptHandle(HandleWrapper&& ListenSocket, HandleWrapper&& AcceptedSocket, std::function<void()>&& OnAccepted) :
731 + ListenSocket(std::move(ListenSocket)), AcceptedSocket(std::move(AcceptedSocket)), OnAccepted(std::move(OnAccepted))
732 +{
733 + Overlapped.hEvent = Event.get();
734 +}
735 +
736 +SingleAcceptHandle::~SingleAcceptHandle()
737 +{
738 + if (State == IOHandleStatus::Pending)
739 + {
740 + LOG_IF_WIN32_BOOL_FALSE(CancelIoEx(ListenSocket.Get(), &Overlapped));
741 +
742 + DWORD bytesProcessed{};
743 + DWORD flagsReturned{};
744 + if (!WSAGetOverlappedResult((SOCKET)ListenSocket.Get(), &Overlapped, &bytesProcessed, TRUE, &flagsReturned))
745 + {
746 + auto error = GetLastError();
747 + LOG_LAST_ERROR_IF(error != ERROR_CONNECTION_ABORTED && error != ERROR_OPERATION_ABORTED);
748 + }
749 + }
750 +}
751 +
752 +void SingleAcceptHandle::Schedule()
753 +{
754 + WI_ASSERT(State == IOHandleStatus::Standby);
755 +
756 + // Schedule the accept.
757 + DWORD bytesReturned{};
758 + if (AcceptEx((SOCKET)ListenSocket.Get(), (SOCKET)AcceptedSocket.Get(), &AcceptBuffer, 0, sizeof(SOCKADDR_STORAGE), sizeof(SOCKADDR_STORAGE), &bytesReturned, &Overlapped))
759 + {
760 + // Accept completed immediately.
761 + State = IOHandleStatus::Completed;
762 + OnAccepted();
763 + }
764 + else
765 + {
766 + auto error = WSAGetLastError();
767 + THROW_HR_IF_MSG(HRESULT_FROM_WIN32(error), error != ERROR_IO_PENDING, "Handle: 0x%p", (void*)ListenSocket.Get());
768 +
769 + State = IOHandleStatus::Pending;
770 + }
771 +}
772 +
773 +void SingleAcceptHandle::Collect()
774 +{
775 + WI_ASSERT(State == IOHandleStatus::Pending);
776 +
777 + DWORD bytesReceived{};
778 + DWORD flagsReturned{};
779 +
780 + THROW_IF_WIN32_BOOL_FALSE(WSAGetOverlappedResult((SOCKET)ListenSocket.Get(), &Overlapped, &bytesReceived, false, &flagsReturned));
781 +
782 + State = IOHandleStatus::Completed;
783 + OnAccepted();
784 +}
785 +
786 +HANDLE SingleAcceptHandle::GetHandle() const
787 +{
788 + return Event.get();
789 +}
\ No newline at end of file
src/windows/common/relay.hpp
+152
@@ -150,4 +150,156 @@ private:
150 std::function<void()> m_onDestroy;
151 };
152
153 +enum class IOHandleStatus
154 +{
155 + Standby,
156 + Pending,
157 + Completed
158 +};
159 +
160 +struct HandleWrapper
161 +{
162 + DEFAULT_MOVABLE(HandleWrapper);
163 + NON_COPYABLE(HandleWrapper)
164 +
165 + HandleWrapper(
166 + wil::unique_handle&& handle, std::function<void()>&& OnClose = []() {}) :
167 + Handle(handle.get()), OwnedHandle(std::move(handle)), OnClose(std::move(OnClose))
168 + {
169 + }
170 +
171 + HandleWrapper(
172 + wil::unique_socket&& handle, std::function<void()>&& OnClose = []() {}) :
173 + Handle((HANDLE)handle.get()), OwnedHandle(wil::unique_socket{handle.release()}), OnClose(std::move(OnClose))
174 + {
175 + }
176 +
177 + HandleWrapper(
178 + wil::unique_event&& handle, std::function<void()>&& OnClose = []() {}) :
179 + Handle(handle.get()), OwnedHandle(wil::unique_handle{handle.release()}), OnClose(std::move(OnClose))
180 + {
181 + }
182 +
183 + HandleWrapper(
184 + SOCKET handle, std::function<void()>&& OnClose = []() {}) :
185 + Handle(reinterpret_cast<HANDLE>(handle)), OnClose(std::move(OnClose))
186 + {
187 + }
188 +
189 + HandleWrapper(HANDLE handle, std::function<void()>&& OnClose = []() {}) : Handle(handle), OnClose(std::move(OnClose))
190 + {
191 + }
192 +
193 + HandleWrapper(
194 + wil::unique_hfile&& handle, std::function<void()>&& OnClose = []() {}) :
195 + Handle(handle.get()), OwnedHandle(wil::unique_handle{handle.release()}), OnClose(std::move(OnClose))
196 + {
197 + }
198 +
199 + ~HandleWrapper()
200 + {
201 + Reset();
202 + }
203 +
204 + HANDLE Get() const
205 + {
206 + return Handle;
207 + }
208 +
209 + void Reset()
210 + {
211 + if (OnClose != nullptr)
212 + {
213 + OnClose();
214 + OnClose = nullptr;
215 + }
216 +
217 + OwnedHandle = {};
218 + Handle = nullptr;
219 + }
220 +
221 +private:
222 + HANDLE Handle{};
223 + std::variant<wil::unique_handle, wil::unique_socket> OwnedHandle;
224 + std::function<void()> OnClose;
225 +};
226 +
227 +class OverlappedIOHandle
228 +{
229 +public:
230 + NON_COPYABLE(OverlappedIOHandle)
231 + NON_MOVABLE(OverlappedIOHandle)
232 +
233 + OverlappedIOHandle() = default;
234 + virtual ~OverlappedIOHandle() = default;
235 + virtual void Schedule() = 0;
236 + virtual void Collect() = 0;
237 + virtual HANDLE GetHandle() const = 0;
238 + IOHandleStatus GetState() const;
239 +
240 +protected:
241 + IOHandleStatus State = IOHandleStatus::Standby;
242 +};
243 +
244 +class EventHandle : public OverlappedIOHandle
245 +{
246 +public:
247 + NON_COPYABLE(EventHandle)
248 + NON_MOVABLE(EventHandle)
249 +
250 + EventHandle(HandleWrapper&& Handle, std::function<void()>&& OnSignalled = []() {});
251 + void Schedule() override;
252 + void Collect() override;
253 + HANDLE GetHandle() const override;
254 +
255 +private:
256 + HandleWrapper Handle;
257 + std::function<void()> OnSignalled;
258 +};
259 +
260 +class SingleAcceptHandle : public OverlappedIOHandle
261 +{
262 +public:
263 + NON_COPYABLE(SingleAcceptHandle)
264 + NON_MOVABLE(SingleAcceptHandle)
265 +
266 + SingleAcceptHandle(HandleWrapper&& ListenSocket, HandleWrapper&& AcceptedSocket, std::function<void()>&& OnAccepted);
267 + ~SingleAcceptHandle();
268 +
269 + void Schedule() override;
270 + void Collect() override;
271 + HANDLE GetHandle() const override;
272 +
273 +private:
274 + HandleWrapper ListenSocket;
275 + HandleWrapper AcceptedSocket;
276 + wil::unique_event Event{wil::EventOptions::ManualReset};
277 + OVERLAPPED Overlapped{};
278 + std::function<void()> OnAccepted;
279 + char AcceptBuffer[2 * sizeof(SOCKADDR_STORAGE)];
280 +};
281 +
282 +class MultiHandleWait
283 +{
284 +public:
285 + enum Flags
286 + {
287 + None = 0,
288 + CancelOnCompleted = 1,
289 + IgnoreErrors = 2
290 + };
291 +
292 + MultiHandleWait() = default;
293 +
294 + void AddHandle(std::unique_ptr<OverlappedIOHandle>&& handle, Flags flags = Flags::None);
295 + bool Run(std::optional<std::chrono::milliseconds> Timeout);
296 + void Cancel();
297 +
298 +private:
299 + std::vector<std::pair<Flags, std::unique_ptr<OverlappedIOHandle>>> m_handles;
300 + bool m_cancel = false;
301 +};
302 +
303 +DEFINE_ENUM_FLAG_OPERATORS(MultiHandleWait::Flags);
304 +
305 } // namespace wsl::windows::common::relay
src/windows/common/socket.cpp
+17 -12
@@ -17,20 +17,25 @@ Abstract:
17 #include "socket.hpp"
18 #pragma hdrstop
19
20 -void wsl::windows::common::socket::Accept(
21 - _In_ SOCKET ListenSocket, _In_ SOCKET Socket, _In_ int Timeout, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)
20 +bool wsl::windows::common::socket::CancellableAccept(
21 + _In_ SOCKET ListenSocket, _In_ SOCKET Socket, _In_ DWORD Timeout, _In_opt_ HANDLE ExitHandle, _In_ const std::source_location& Location)
22 {
23 - CHAR AcceptBuffer[2 * sizeof(SOCKADDR_STORAGE)]{};
24 - DWORD BytesReturned;
25 - OVERLAPPED Overlapped{};
26 - const wil::unique_event OverlappedEvent(wil::EventOptions::ManualReset);
27 - Overlapped.hEvent = OverlappedEvent.get();
28 - const BOOL Success =
29 - AcceptEx(ListenSocket, Socket, AcceptBuffer, 0, sizeof(SOCKADDR_STORAGE), sizeof(SOCKADDR_STORAGE), &BytesReturned, &Overlapped);
23 + relay::MultiHandleWait io;
24 +
25 + bool accepted = false;
26 +
27 + io.AddHandle(std::make_unique<relay::SingleAcceptHandle>(ListenSocket, Socket, [&]() { accepted = true; }), relay::MultiHandleWait::CancelOnCompleted);
28 +
29 + if (ExitHandle != nullptr)
30 + {
31 + io.AddHandle(std::make_unique<relay::EventHandle>(ExitHandle), relay::MultiHandleWait::CancelOnCompleted);
32 + }
33 +
34 + io.Run(std::chrono::milliseconds(Timeout));
35
31 - if (!Success)
36 + if (!accepted)
37 {
33 - GetResult(ListenSocket, Overlapped, Timeout, ExitHandle, Location);
38 + return false; // Accept was cancelled by the exit event.
39 }
40
41 // Set the accept context to mark the socket as connected.
@@ -39,7 +44,7 @@ void wsl::windows::common::socket::Accept(
44 "From: %hs",
45 std::format("{}", Location).c_str());
46
42 - return;
47 + return true;
48 }
49
50 std::pair<DWORD, DWORD> wsl::windows::common::socket::GetResult(
src/windows/common/socket.hpp
+2 -2
@@ -18,10 +18,10 @@ Abstract:
18
19 namespace wsl::windows::common::socket {
20
21 -void Accept(
21 +bool CancellableAccept(
22 _In_ SOCKET ListenSocket,
23 _In_ SOCKET Socket,
24 - _In_ int Timeout,
24 + _In_ DWORD Timeout,
25 _In_opt_ HANDLE ExitHandle,
26 _In_ const std::source_location& Location = std::source_location::current());
27
src/windows/service/exe/WslCoreVm.cpp
+20 -12
@@ -823,14 +823,15 @@ WslCoreVm::~WslCoreVm() noexcept
823
824 wil::unique_socket WslCoreVm::AcceptConnection(_In_ DWORD ReceiveTimeout, _In_ const std::source_location& Location) const
825 {
826 - auto socket =
827 - wsl::windows::common::hvsocket::Accept(m_listenSocket.get(), m_vmConfig.KernelBootTimeout, m_terminatingEvent.get(), Location);
826 + auto socket = hvsocket::CancellableAccept(m_listenSocket.get(), m_vmConfig.KernelBootTimeout, m_terminatingEvent.get(), Location);
827 + THROW_HR_IF(E_ABORT, !socket.has_value());
828 +
829 if (ReceiveTimeout != 0)
830 {
830 - THROW_LAST_ERROR_IF(setsockopt(socket.get(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&ReceiveTimeout, sizeof(ReceiveTimeout)) == SOCKET_ERROR);
831 + THROW_LAST_ERROR_IF(setsockopt(socket->get(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&ReceiveTimeout, sizeof(ReceiveTimeout)) == SOCKET_ERROR);
832 }
833
833 - return socket;
834 + return std::move(socket.value());
835 }
836
837 _Requires_lock_held_(m_guestDeviceLock)
@@ -1084,13 +1085,16 @@ void WslCoreVm::CollectCrashDumps(wil::unique_socket&& listenSocket) const
1085 {
1086 try
1087 {
1087 - auto socket = wsl::windows::common::hvsocket::Accept(listenSocket.get(), INFINITE, m_terminatingEvent.get());
1088 + auto socket = hvsocket::CancellableAccept(listenSocket.get(), INFINITE, m_terminatingEvent.get());
1089 + if (!socket.has_value())
1090 + {
1091 + break; // VM is exiting.
1092 + }
1093
1094 DWORD receiveTimeout = m_vmConfig.KernelBootTimeout;
1090 - THROW_LAST_ERROR_IF(
1091 - setsockopt(listenSocket.get(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&receiveTimeout, sizeof(receiveTimeout)) == SOCKET_ERROR);
1095 + THROW_LAST_ERROR_IF(setsockopt(socket->get(), SOL_SOCKET, SO_RCVTIMEO, (const char*)&receiveTimeout, sizeof(receiveTimeout)) == SOCKET_ERROR);
1096
1093 - auto channel = wsl::shared::SocketChannel{std::move(socket), "crash_dump", m_terminatingEvent.get()};
1097 + auto channel = wsl::shared::SocketChannel{std::move(socket.value()), "crash_dump", m_terminatingEvent.get()};
1098
1099 const auto& message = channel.ReceiveMessage<LX_PROCESS_CRASH>();
1100 const char* process = reinterpret_cast<const char*>(&message.Buffer);
@@ -2554,10 +2558,14 @@ try
2558 for (;;)
2559 {
2560 // Create a worker thread to handle each request.
2557 - wsl::shared::SocketChannel channel{
2558 - wsl::windows::common::hvsocket::Accept(listenSocket.get(), INFINITE, m_terminatingEvent.get()),
2559 - "VirtioFs",
2560 - m_terminatingEvent.get()};
2561 +
2562 + auto socket = hvsocket::CancellableAccept(listenSocket.get(), INFINITE, m_terminatingEvent.get());
2563 + if (!socket.has_value())
2564 + {
2565 + break;
2566 + }
2567 +
2568 + wsl::shared::SocketChannel channel{std::move(socket.value()), "VirtioFs", m_terminatingEvent.get()};
2569 std::thread([this, channel = std::move(channel)]() mutable {
2570 try
2571 {
src/windows/wslrelay/localhost.cpp
+5 -1
@@ -297,7 +297,11 @@ try
297 wil::unique_socket InetSocket(WSASocket(AddressFamily, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));
298 THROW_LAST_ERROR_IF(!InetSocket);
299
300 - wsl::windows::common::socket::Accept(Arguments->ListenSocket.get(), InetSocket.get(), INFINITE, Arguments->ExitEvent.get());
300 + if (!wsl::windows::common::socket::CancellableAccept(
301 + Arguments->ListenSocket.get(), InetSocket.get(), INFINITE, Arguments->ExitEvent.get()))
302 + {
303 + break; // Exit event was signaled, exit.
304 + }
305
306 // Establish a relay thread.
307
src/windows/wslrelay/main.cpp
+4 -1
@@ -120,7 +120,10 @@ try
120 const wil::unique_socket socket(WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED));
121 THROW_LAST_ERROR_IF(!socket);
122
123 - wsl::windows::common::socket::Accept(listenSocket.get(), socket.get(), INFINITE, exitEvent.get());
123 + if (!wsl::windows::common::socket::CancellableAccept(listenSocket.get(), socket.get(), INFINITE, exitEvent.get()))
124 + {
125 + return 1;
126 + }
127
128 // Begin the relay.
129 wsl::windows::common::relay::BidirectionalRelay(