diff --git a/src/windows/service/exe/WslCoreInstance.cpp b/src/windows/service/exe/WslCoreInstance.cpp index 844f536cc..10b187849 100644 --- a/src/windows/service/exe/WslCoreInstance.cpp +++ b/src/windows/service/exe/WslCoreInstance.cpp @@ -173,13 +173,22 @@ 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 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); + 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/src/windows/service/exe/WslCoreInstance.h b/src/windows/service/exe/WslCoreInstance.h index bf2adc2cc..782a5c7df 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 035323b75..7f9d167c0 100644 --- a/test/windows/UnitTests.cpp +++ b/test/windows/UnitTests.cpp @@ -4747,6 +4747,49 @@ 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()); + VERIFY_ARE_EQUAL(terminationProcessBuilder.Run(terminationTimeout), 0u); + + VERIFY_ARE_EQUAL(waitingResult, WAIT_TIMEOUT); + 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));