From 4831c315a341823923d5b4b8fbc947c5f3aca186 Mon Sep 17 00:00:00 2001 From: SylvainM98 <70053483+SylvainM98@users.noreply.github.com> Date: Wed, 19 Aug 2026 16:43:03 +0200 Subject: [PATCH 1/2] Avoid holding the instance lock while waiting for OOBE --- src/windows/service/exe/WslCoreInstance.cpp | 15 +++++- test/windows/UnitTests.cpp | 51 +++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/windows/service/exe/WslCoreInstance.cpp b/src/windows/service/exe/WslCoreInstance.cpp index 844f536cc9..a5584accf2 100644 --- a/src/windows/service/exe/WslCoreInstance.cpp +++ b/src/windows/service/exe/WslCoreInstance.cpp @@ -173,13 +173,24 @@ void WslCoreInstance::CreateLxProcess( // Ensure the instance is still running. - std::lock_guard lock(m_lock); + std::unique_lock lock(m_lock); THROW_HR_IF(HCS_E_TERMINATED, (!m_initChannel || !m_consoleManager)); if (m_oobeCompleteEvent && !m_oobeCompleteEvent.is_signaled()) { EMIT_USER_WARNING(wsl::shared::Localization::MessageWaitingForOobe(m_configuration.Name.c_str())); - m_oobeCompleteEvent.wait(); + + const wil::unique_handle oobeCompleteEvent{ + wsl::windows::common::wslutil::DuplicateHandle(m_oobeCompleteEvent.get(), SYNCHRONIZE)}; + const wil::unique_handle destroyingEvent{ + wsl::windows::common::wslutil::DuplicateHandle(m_destroyingEvent.get(), SYNCHRONIZE)}; + const HANDLE waitHandles[] = {oobeCompleteEvent.get(), destroyingEvent.get()}; + lock.unlock(); + const DWORD waitResult = WaitForMultipleObjects(RTL_NUMBER_OF(waitHandles), waitHandles, FALSE, INFINITE); + THROW_LAST_ERROR_IF(waitResult == WAIT_FAILED); + lock.lock(); + THROW_HR_IF(HCS_E_TERMINATED, waitResult == WAIT_OBJECT_0 + 1 || !m_initChannel || !m_consoleManager); + THROW_HR_IF(E_UNEXPECTED, waitResult != WAIT_OBJECT_0); } // Initialize the create process message. diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index 035323b75a..6445038945 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -4747,6 +4747,57 @@ VERSION_ID="Invalid|Format" TerminateDistribution(); } + WSL2_TEST_METHOD(ModernOOBETermination) + { + constexpr DWORD oobeDurationSeconds = 30; + constexpr DWORD waiterObservationTimeout = 1000; + constexpr DWORD terminationTimeout = 10000; + constexpr DWORD cleanupTimeout = 45000; + + const auto lxssKey = wsl::windows::common::registry::OpenLxssUserKey(); + const auto testDistroId = GetDistributionId(LXSS_DISTRO_NAME_TEST_L); + VERIFY_IS_TRUE(testDistroId.has_value()); + const auto testDistroIdString = wsl::shared::string::GuidToString(testDistroId.value()); + + DistroFileChange distributionconf(L"/etc/wsl-distribution.conf", false); + distributionconf.SetContent( + std::format(L"[oobe]\ncommand = /bin/bash -c 'echo OOBE_STARTED; sleep {}'\n", oobeDurationSeconds).c_str()); + + RegistryKeyChange runOOBE(lxssKey.get(), testDistroIdString.c_str(), L"RunOOBE", 1); + TerminateDistribution(); + + auto [oobeOutputRead, oobeOutputWrite] = CreateSubprocessPipe(false, true); + wsl::windows::common::SubProcess oobeProcessBuilder( + nullptr, LxssGenerateWslCommandLine(L"-d " LXSS_DISTRO_NAME_TEST_L).c_str()); + oobeProcessBuilder.SetStdHandles(nullptr, oobeOutputWrite.get(), oobeOutputWrite.get()); + const auto oobeProcess = oobeProcessBuilder.Start(); + oobeOutputWrite.reset(); + + PartialHandleRead oobeOutput(oobeOutputRead.get()); + oobeOutput.Expect("OOBE_STARTED\n"); + + wsl::windows::common::SubProcess waitingProcessBuilder( + nullptr, LxssGenerateWslCommandLine(L"-d " LXSS_DISTRO_NAME_TEST_L L" true").c_str()); + const auto waitingProcess = waitingProcessBuilder.Start(); + const DWORD waitingResult = WaitForSingleObject(waitingProcess.get(), waiterObservationTimeout); + + wsl::windows::common::SubProcess terminationProcessBuilder( + nullptr, LxssGenerateWslCommandLine(L"--terminate " LXSS_DISTRO_NAME_TEST_L).c_str()); + const auto terminationProcess = terminationProcessBuilder.Start(); + const DWORD terminationResult = WaitForSingleObject(terminationProcess.get(), terminationTimeout); + + if (terminationResult == WAIT_TIMEOUT) + { + VERIFY_ARE_EQUAL(WaitForSingleObject(terminationProcess.get(), cleanupTimeout), WAIT_OBJECT_0); + } + + VERIFY_ARE_EQUAL(waitingResult, WAIT_TIMEOUT); + VERIFY_ARE_EQUAL(terminationResult, WAIT_OBJECT_0); + VERIFY_ARE_EQUAL(wsl::windows::common::SubProcess::GetExitCode(terminationProcess.get(), 0), 0u); + VERIFY_ARE_EQUAL(WaitForSingleObject(waitingProcess.get(), cleanupTimeout), WAIT_OBJECT_0); + VERIFY_ARE_EQUAL(WaitForSingleObject(oobeProcess.get(), cleanupTimeout), WAIT_OBJECT_0); + } + static void ValidateDistributionStarts(LPCWSTR Name) { auto [out, _] = LxsstuLaunchWslAndCaptureOutput(std::format(L"-d {} echo -n OK", Name)); From 44207c8e0d38b5fe919aa3974b5aae117e359daf Mon Sep 17 00:00:00 2001 From: SylvainM98 <70053483+SylvainM98@users.noreply.github.com> Date: Thu, 20 Aug 2026 08:37:19 +0200 Subject: [PATCH 2/2] Address OOBE shutdown review feedback --- src/windows/service/exe/WslCoreInstance.cpp | 6 ++---- src/windows/service/exe/WslCoreInstance.h | 4 ++-- test/windows/UnitTests.cpp | 10 +--------- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/windows/service/exe/WslCoreInstance.cpp b/src/windows/service/exe/WslCoreInstance.cpp index a5584accf2..10b1878497 100644 --- a/src/windows/service/exe/WslCoreInstance.cpp +++ b/src/windows/service/exe/WslCoreInstance.cpp @@ -180,10 +180,8 @@ void WslCoreInstance::CreateLxProcess( { EMIT_USER_WARNING(wsl::shared::Localization::MessageWaitingForOobe(m_configuration.Name.c_str())); - const wil::unique_handle oobeCompleteEvent{ - wsl::windows::common::wslutil::DuplicateHandle(m_oobeCompleteEvent.get(), SYNCHRONIZE)}; - const wil::unique_handle destroyingEvent{ - wsl::windows::common::wslutil::DuplicateHandle(m_destroyingEvent.get(), SYNCHRONIZE)}; + const auto oobeCompleteEvent = m_oobeCompleteEvent; + const auto destroyingEvent = m_destroyingEvent; const HANDLE waitHandles[] = {oobeCompleteEvent.get(), destroyingEvent.get()}; lock.unlock(); const DWORD waitResult = WaitForMultipleObjects(RTL_NUMBER_OF(waitHandles), waitHandles, FALSE, INFINITE); diff --git a/src/windows/service/exe/WslCoreInstance.h b/src/windows/service/exe/WslCoreInstance.h index bf2adc2cca..782a5c7dfd 100644 --- a/src/windows/service/exe/WslCoreInstance.h +++ b/src/windows/service/exe/WslCoreInstance.h @@ -139,6 +139,6 @@ class WslCoreInstance : public LxssRunningInstance DWORD m_socketTimeout{}; HANDLE m_jobObject{}; std::thread m_oobeThread; - wil::unique_event m_destroyingEvent{wil::EventOptions::ManualReset}; - wil::unique_event m_oobeCompleteEvent; + wil::shared_event m_destroyingEvent{wil::EventOptions::ManualReset}; + wil::shared_event m_oobeCompleteEvent; }; diff --git a/test/windows/UnitTests.cpp b/test/windows/UnitTests.cpp index 6445038945..7f9d167c0d 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -4783,17 +4783,9 @@ VERSION_ID="Invalid|Format" wsl::windows::common::SubProcess terminationProcessBuilder( nullptr, LxssGenerateWslCommandLine(L"--terminate " LXSS_DISTRO_NAME_TEST_L).c_str()); - const auto terminationProcess = terminationProcessBuilder.Start(); - const DWORD terminationResult = WaitForSingleObject(terminationProcess.get(), terminationTimeout); - - if (terminationResult == WAIT_TIMEOUT) - { - VERIFY_ARE_EQUAL(WaitForSingleObject(terminationProcess.get(), cleanupTimeout), WAIT_OBJECT_0); - } + VERIFY_ARE_EQUAL(terminationProcessBuilder.Run(terminationTimeout), 0u); VERIFY_ARE_EQUAL(waitingResult, WAIT_TIMEOUT); - VERIFY_ARE_EQUAL(terminationResult, WAIT_OBJECT_0); - VERIFY_ARE_EQUAL(wsl::windows::common::SubProcess::GetExitCode(terminationProcess.get(), 0), 0u); VERIFY_ARE_EQUAL(WaitForSingleObject(waitingProcess.get(), cleanupTimeout), WAIT_OBJECT_0); VERIFY_ARE_EQUAL(WaitForSingleObject(oobeProcess.get(), cleanupTimeout), WAIT_OBJECT_0); }