Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1beaebc
Use event instead of termination callback
kvega005 Jun 10, 2026
c9da34e
Fux build
kvega005 Jun 10, 2026
2fb90c0
Fix
kvega005 Jun 10, 2026
be6b59c
Address feedback
kvega005 Jun 10, 2026
9491b63
Merge remote-tracking branch 'origin/master' into terminateEvent
kvega005 Jun 11, 2026
d45f540
Merge branch 'master' into terminateEvent
kvega005 Jun 11, 2026
c6a975e
Merge branch 'master' into terminateEvent
kvega005 Jun 11, 2026
aa4bd4b
Address feedback.
kvega005 Jun 11, 2026
acd9ccb
Merge branch 'terminateEvent' of https://github.com/kvega005/WSL into…
kvega005 Jun 11, 2026
7f173d9
Merge branch 'master' into terminateEvent
kvega005 Jun 11, 2026
3563255
wslc: idle-terminate per-user VMs when no containers are active
Jun 10, 2026
69f53af
wslcsession: fix idle-VM runtime races (factory proxy, exit code, rm …
Jun 10, 2026
d51eab1
wslcsession: keep VM alive across CLI container op via BeginContainer…
Jun 10, 2026
c3554ea
wslcsession: add idle grace period before tearing down an idle VM
Jun 10, 2026
38e1ba3
wslcsession: keep VM alive for the lifetime of a root-namespace process
Jun 11, 2026
f38c556
wslcsession: apply clang-format 19.1.5 formatting
Jun 11, 2026
7a08418
wslcsession: update WSLCSession class comment for factory-based lazy …
Jun 11, 2026
5e599bd
wslcsession: resolve rebase artifacts from termination-event integration
Jun 11, 2026
3a01811
wslc: remove dead TerminationCallback plumbing from VM factory
Jun 11, 2026
80282b5
fix ~HcsVirtualMachine deadlock with OnExit on m_lock
Jun 11, 2026
6d43b6a
Preserve recovery warnings in lazy-VM model
Jun 12, 2026
cb8e240
Fix lazy-VM idle worker serializing concurrent operations
Jun 12, 2026
2987fa8
Adapt SessionEnter storage-not-found E2E to eager friendly message
Jun 12, 2026
46eda9e
Guard idle worker thread entrypoint against unhandled exceptions
Jun 12, 2026
ea2254c
Pin VM while client holds container/process proxies
Jun 12, 2026
4f5a23c
Wake idle worker via captured idle state, not raw session ref
Jun 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions msipackage/package.wix.in
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,6 @@
</RegistryKey>
</RegistryKey>

<!-- ITerminationCallback-->
<RegistryKey Root="HKCR" Key="Interface\{7BC4E198-6531-4FA6-ADE2-5EF3D2A04DFE}">
<RegistryValue Value="ITerminationCallback" Type="string" />
<RegistryKey Key="ProxyStubClsid32">
<RegistryValue Value="{4EA0C6DD-E9FF-48E7-994E-13A31D10DC60}" Type="string" />
</RegistryKey>
</RegistryKey>

<!-- ICrashDumpCallback-->
<RegistryKey Root="HKCR" Key="Interface\{8C5A7B14-9D26-4FAE-AB31-7E5BC23F4801}">
<RegistryValue Value="ICrashDumpCallback" Type="string" />
Expand Down
2 changes: 0 additions & 2 deletions src/windows/WslcSDK/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
set(SOURCES
IOCallback.cpp
ProgressCallback.cpp
TerminationCallback.cpp
CrashDumpCallback.cpp
wslcsdk.cpp
WslcsdkPrivate.cpp
Expand All @@ -10,7 +9,6 @@ set(HEADERS
Defaults.h
IOCallback.h
ProgressCallback.h
TerminationCallback.h
CrashDumpCallback.h
wslcsdk.h
WslcsdkPrivate.h
Expand Down
58 changes: 0 additions & 58 deletions src/windows/WslcSDK/TerminationCallback.cpp

This file was deleted.

32 changes: 0 additions & 32 deletions src/windows/WslcSDK/TerminationCallback.h

This file was deleted.

3 changes: 0 additions & 3 deletions src/windows/WslcSDK/WslcsdkPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ typedef struct WslcSessionOptionsInternal

WslcVhdRequirements vhdRequirements;
WslcSessionFeatureFlags featureFlags;
WslcSessionTerminationCallback terminationCallback;
PVOID terminationCallbackContext;
} WslcSessionOptionsInternal;

static_assert(sizeof(WslcSessionOptionsInternal) == WSLC_SESSION_OPTIONS_SIZE, "WSLC_SESSION_OPTIONS_INTERNAL size mismatch");
Expand Down Expand Up @@ -107,7 +105,6 @@ const WslcContainerOptionsInternal* GetInternalType(const WslcContainerSettings*
struct WslcSessionImpl
{
wil::com_ptr<IWSLCSession> session;
wil::com_ptr<ITerminationCallback> terminationCallback;
};

WslcSessionImpl* GetInternalType(WslcSession handle);
Expand Down
14 changes: 11 additions & 3 deletions src/windows/WslcSDK/winrt/Session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,16 @@ void Session::Start()
throw winrt::hresult_illegal_method_call(L"Session has already been started");
}

winrt::check_hresult(WslcSetSessionSettingsTerminationCallback(GetStructPointer(m_settings), TerminatedCallback, /* context */ this));

wil::unique_cotaskmem_string errorMessage;
auto hr = WslcCreateSession(GetStructPointer(m_settings), m_session.put(), errorMessage.put());
THROW_MSG_IF_FAILED(hr, errorMessage);
m_settings = nullptr;

winrt::check_hresult(WslcGetSessionTerminationEvent(m_session.get(), m_terminationEvent.put()));

m_terminationWait.reset(CreateThreadpoolWait(&Session::OnTerminated, this, nullptr));
THROW_LAST_ERROR_IF_NULL(m_terminationWait);
SetThreadpoolWait(m_terminationWait.get(), m_terminationEvent.get(), nullptr);
}

void Session::EnsureStarted() const
Expand Down Expand Up @@ -300,11 +304,15 @@ WslcSession Session::ToHandle()
return m_session.get();
}

void CALLBACK Session::TerminatedCallback(_In_ WslcSessionTerminationReason reason, _In_opt_ PVOID context) noexcept
void CALLBACK Session::OnTerminated(PTP_CALLBACK_INSTANCE /* instance */, PVOID context, PTP_WAIT /* wait */, TP_WAIT_RESULT /* waitResult */) noexcept
{
try
{
auto session = static_cast<Session*>(context);

WslcSessionTerminationReason reason = WSLC_SESSION_TERMINATION_REASON_UNKNOWN;
LOG_IF_FAILED(WslcGetSessionTerminationReason(session->m_session.get(), &reason));

session->m_terminatedEvent(static_cast<SessionTerminationReason>(reason));
}
CATCH_LOG();
Expand Down
9 changes: 6 additions & 3 deletions src/windows/WslcSDK/winrt/Session.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,15 @@ struct Session : SessionT<Session>
void EnsureStarted() const;
winrt::Microsoft::WSL::Containers::SessionSettings m_settings; // Only kept until Start() is called

static void CALLBACK TerminatedCallback(_In_ WslcSessionTerminationReason reason, _In_opt_ PVOID context) noexcept;
// Threadpool callback that raises the Terminated event once the session's termination handle is signaled.
static void CALLBACK OnTerminated(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WAIT wait, TP_WAIT_RESULT waitResult) noexcept;

// Releasing the session handle may trigger the termination callback.
// Keep these two in this order so that the session handle is released before the termination event is destructed.
winrt::event<winrt::Microsoft::WSL::Containers::SessionTerminationHandler> m_terminatedEvent;
wil::unique_any<WslcSession, decltype(&WslcReleaseSession), &WslcReleaseSession> m_session{nullptr};

// Bridges the one-off termination event surfaced by the SDK to the WinRT Terminated event.
wil::unique_handle m_terminationEvent;
wil::unique_threadpool_wait m_terminationWait;
};
} // namespace winrt::Microsoft::WSL::Containers::implementation
namespace winrt::Microsoft::WSL::Containers::factory_implementation {
Expand Down
46 changes: 30 additions & 16 deletions src/windows/WslcSDK/wslcsdk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ Module Name:
#include "WslcsdkPrivate.h"
#include "Defaults.h"
#include "ProgressCallback.h"
#include "TerminationCallback.h"
#include "CrashDumpCallback.h"
#include "Localization.h"
#include "WslInstall.h"
Expand Down Expand Up @@ -435,12 +434,6 @@ try
runtimeSettings.MemoryMb = internalType->memoryMb;
runtimeSettings.BootTimeoutMs = internalType->timeoutMS;
runtimeSettings.NetworkingMode = WSLCNetworkingModeVirtioProxy;
auto terminationCallback = TerminationCallback::CreateIf(internalType);
if (terminationCallback)
{
result->terminationCallback.attach(terminationCallback.as<ITerminationCallback>().detach());
runtimeSettings.TerminationCallback = terminationCallback.get();
}
runtimeSettings.FeatureFlags = ConvertFlags(internalType->featureFlags);
WI_SetFlag(runtimeSettings.FeatureFlags, WslcFeatureFlagsVirtioFs);
WI_SetFlag(runtimeSettings.FeatureFlags, WslcFeatureFlagsDnsTunneling);
Expand Down Expand Up @@ -587,15 +580,39 @@ try
}
CATCH_RETURN();

STDAPI WslcSetSessionSettingsTerminationCallback(
_In_ WslcSessionSettings* sessionSettings, _In_opt_ WslcSessionTerminationCallback terminationCallback, _In_opt_ PVOID terminationContext)
STDAPI WslcGetSessionTerminationEvent(_In_ WslcSession session, _Out_ HANDLE* terminationEvent)
try
{
auto internalType = CheckAndGetInternalType(sessionSettings);
RETURN_HR_IF(E_INVALIDARG, terminationCallback == nullptr && terminationContext != nullptr);
RETURN_HR_IF_NULL(E_POINTER, terminationEvent);
*terminationEvent = nullptr;

auto internalType = CheckAndGetInternalType(session);
RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);

RETURN_HR(internalType->session->GetTerminationEvent(terminationEvent));
}
CATCH_RETURN();

STDAPI WslcGetSessionTerminationReason(_In_ WslcSession session, _Out_ WslcSessionTerminationReason* reason)
try
{
static_assert(
WSLC_SESSION_TERMINATION_REASON_UNKNOWN == WSLCVirtualMachineTerminationReasonUnknown &&
WSLC_SESSION_TERMINATION_REASON_SHUTDOWN == WSLCVirtualMachineTerminationReasonShutdown &&
WSLC_SESSION_TERMINATION_REASON_CRASHED == WSLCVirtualMachineTerminationReasonCrashed,
"Termination reason enum values mismatch.");

RETURN_HR_IF_NULL(E_POINTER, reason);
*reason = WSLC_SESSION_TERMINATION_REASON_UNKNOWN;

auto internalType = CheckAndGetInternalType(session);
RETURN_HR_IF_NULL(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), internalType->session);

WSLCVirtualMachineTerminationReason runtimeReason = WSLCVirtualMachineTerminationReasonUnknown;
wil::unique_cotaskmem_string details;
RETURN_IF_FAILED(internalType->session->GetTerminationReason(&runtimeReason, &details));

internalType->terminationCallback = terminationCallback;
internalType->terminationCallbackContext = terminationContext;
*reason = static_cast<WslcSessionTerminationReason>(runtimeReason);

return S_OK;
}
Expand Down Expand Up @@ -649,10 +666,7 @@ try
{
auto internalType = CheckAndGetInternalTypeUniquePointer(session);

// Drop the session before the termination callback, in case session destruction triggers
// the termination callback.
internalType->session.reset();
internalType->terminationCallback.reset();

return S_OK;
}
Expand Down
3 changes: 2 additions & 1 deletion src/windows/WslcSDK/wslcsdk.def
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ WslcReleaseContainer
WslcReleaseProcess

WslcSetSessionSettingsFeatureFlags
WslcSetSessionSettingsTerminationCallback
WslcGetSessionTerminationEvent
WslcGetSessionTerminationReason
WslcSetSessionSettingsCpuCount
WslcSetSessionSettingsMemory
WslcSetSessionSettingsTimeout
Expand Down
9 changes: 3 additions & 6 deletions src/windows/WslcSDK/wslcsdk.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ EXTERN_C_START
#define WSLC_E_REGISTRY_BLOCKED_BY_POLICY MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, WSLC_E_BASE + 13) /* 0x8004060D */

// Session values
#define WSLC_SESSION_OPTIONS_SIZE 88
#define WSLC_SESSION_OPTIONS_SIZE 72
#define WSLC_SESSION_OPTIONS_ALIGNMENT 8

typedef struct WslcSessionSettings
Expand Down Expand Up @@ -123,8 +123,6 @@ typedef enum WslcSessionTerminationReason
WSLC_SESSION_TERMINATION_REASON_CRASHED = 2,
} WslcSessionTerminationReason;

typedef __callback void(CALLBACK* WslcSessionTerminationCallback)(_In_ WslcSessionTerminationReason reason, _In_opt_ PVOID context);

typedef struct WslcSessionCrashDumpInfo
{
_Field_z_ PCWSTR dumpPath;
Expand Down Expand Up @@ -153,9 +151,8 @@ STDAPI WslcSetSessionSettingsVhd(_In_ WslcSessionSettings* sessionSettings, _In_

STDAPI WslcSetSessionSettingsFeatureFlags(_In_ WslcSessionSettings* sessionSettings, _In_ WslcSessionFeatureFlags flags);

// Pass in Null for callback to clear the termination callback
STDAPI WslcSetSessionSettingsTerminationCallback(
_In_ WslcSessionSettings* sessionSettings, _In_opt_ WslcSessionTerminationCallback terminationCallback, _In_opt_ PVOID terminationContext);
STDAPI WslcGetSessionTerminationEvent(_In_ WslcSession session, _Out_ HANDLE* terminationEvent);
STDAPI WslcGetSessionTerminationReason(_In_ WslcSession session, _Out_ WslcSessionTerminationReason* reason);

STDAPI WslcTerminateSession(_In_ WslcSession session);
STDAPI WslcReleaseSession(_In_ WslcSession session);
Expand Down
37 changes: 24 additions & 13 deletions src/windows/service/exe/HcsVirtualMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,12 +285,6 @@ HcsVirtualMachine::HcsVirtualMachine(_In_ const WSLCSessionSettings* Settings)
m_guestDeviceManager = std::make_shared<::GuestDeviceManager>(m_vmIdString, m_vmId);
}

// Configure termination callback
if (Settings->TerminationCallback)
{
m_terminationCallback = Settings->TerminationCallback;
}

hcs::RegisterCallback(m_computeSystem.get(), &HcsVirtualMachine::OnVmExitCallback, this);

// Create a listening socket for mini_init to connect to once the VM is running.
Expand Down Expand Up @@ -319,7 +313,11 @@ HcsVirtualMachine::HcsVirtualMachine(_In_ const WSLCSessionSettings* Settings)

HcsVirtualMachine::~HcsVirtualMachine()
{
std::lock_guard lock(m_lock);
// N.B. Do not hold m_lock here. OnExit() acquires m_lock to cache the termination reason
// before signaling m_vmExitEvent, and closing the compute system below drains any in-flight
// HCS exit/crash callbacks. Holding m_lock across the exit-event wait and HcsCloseComputeSystem
// would deadlock against an in-flight OnExit() that is blocked acquiring m_lock. By the time the
// compute system is closed no further callbacks can run, so the remaining teardown is unguarded.

// Wait up to 5 seconds for the VM to terminate gracefully.
bool forceTerminate = false;
Expand Down Expand Up @@ -692,8 +690,6 @@ CATCH_LOG()

void HcsVirtualMachine::OnExit(const HCS_EVENT* Event)
{
m_vmExitEvent.SetEvent();

const auto exitStatus = wsl::shared::FromJson<wsl::windows::common::hcs::SystemExitStatus>(Event->EventData);

auto reason = WSLCVirtualMachineTerminationReasonUnknown;
Expand All @@ -715,12 +711,29 @@ void HcsVirtualMachine::OnExit(const HCS_EVENT* Event)
}
}

if (m_terminationCallback)
// Cache the termination reason and details before signaling the exit event.
{
LOG_IF_FAILED(m_terminationCallback->OnTermination(reason, Event->EventData));
std::lock_guard lock(m_lock);
m_terminationReason = reason;
m_terminationDetails = Event->EventData;
}

m_vmExitEvent.SetEvent();
}

HRESULT HcsVirtualMachine::GetTerminationReason(_Out_ WSLCVirtualMachineTerminationReason* Reason, _Out_ LPWSTR* Details)
try
{
RETURN_HR_IF(E_POINTER, Reason == nullptr || Details == nullptr);

std::lock_guard lock(m_lock);
*Reason = m_terminationReason;
*Details = wil::make_cotaskmem_string(m_terminationDetails.c_str()).release();

return S_OK;
}
CATCH_RETURN()

void HcsVirtualMachine::OnCrash(const HCS_EVENT* Event)
{
if (m_crashLogCaptured.load() && m_vmSavedStateCaptured.load())
Expand Down Expand Up @@ -863,7 +876,6 @@ WSLCVirtualMachineFactory::WSLCVirtualMachineFactory(_In_ const WSLCSessionSetti
m_dmesgOutput.reset(wslutil::DuplicateHandle(wslutil::FromCOMInputHandle(Settings->DmesgOutput), GENERIC_WRITE | SYNCHRONIZE));
}

m_terminationCallback = Settings->TerminationCallback;
m_maximumStorageSizeMb = Settings->MaximumStorageSizeMb;
m_cpuCount = Settings->CpuCount;
m_memoryMb = Settings->MemoryMb;
Expand All @@ -883,7 +895,6 @@ WSLCSessionSettings WSLCVirtualMachineFactory::BuildSettings()
settings.MemoryMb = m_memoryMb;
settings.BootTimeoutMs = m_bootTimeoutMs;
settings.NetworkingMode = m_networkingMode;
settings.TerminationCallback = m_terminationCallback.get();
settings.FeatureFlags = m_featureFlags;
settings.StorageFlags = m_storageFlags;
settings.RootVhdOverride = m_rootVhdOverride ? m_rootVhdOverride->c_str() : nullptr;
Expand Down
Loading