Skip to content

Add job objects to terminate child processes on VM shutdown#40607

Merged
benhillis merged 5 commits into
masterfrom
benhill/job-object-child-processes
May 21, 2026
Merged

Add job objects to terminate child processes on VM shutdown#40607
benhillis merged 5 commits into
masterfrom
benhill/job-object-child-processes

Conversation

@benhillis
Copy link
Copy Markdown
Member

Summary

Add JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE job objects to ensure that child processes spawned by the service (wsldevicehost, wslhost.exe, wslrelay.exe) are terminated when the VM shuts down. This prevents lingering processes from holding DLLs locked during package upgrades.

PR Checklist

  • Applies to: WSL Windows components
  • Tests: Existing tests pass (cmake --build . -- -m succeeds)

Detailed Description

Problem

During package upgrade, wsldevicehost.dll (and potentially wslhost.exe/wslrelay.exe) can remain running after the service exits, holding file locks that prevent the installer from replacing binaries.

Solution

Create per-VM job objects with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE:

  • DeviceHostProxy: Creates a job object and assigns the COM device host process (received via RegisterDeviceHost PID) using AssignProcessToJobObject.
  • WslCoreVm: Creates a job object and passes it to child process launches (debug console, kd relay, port relay, interop server) via PROC_THREAD_ATTRIBUTE_JOB_LIST.
  • SubProcess: New SetJobObject() method that uses PROC_THREAD_ATTRIBUTE_JOB_LIST for clean job assignment at process creation time.
  • helpers: All Launch* functions gain an optional HANDLE JobObject parameter (defaults to nullptr for backward compatibility).

When the VM is destroyed, the job handle closes and Windows automatically terminates all associated processes.

Validation Steps

  1. Build succeeds: cmake --build . -- -m (wslservice.exe, wsl.exe)
  2. Verified no existing callers are broken (all new parameters default to nullptr)
  3. Code review confirmed lifetime safety and correctness

Copilot AI review requested due to automatic review settings May 20, 2026 17:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces per-VM Windows Job Objects configured with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE so that processes spawned by the WSL service (e.g., wslhost.exe, wslrelay.exe, and device host processes) are automatically terminated when the VM shuts down, preventing lingering processes from keeping package binaries/DLLs locked during upgrades.

Changes:

  • Add a per-VM job object in WslCoreVm and pass it through process-launch helpers so VM-scoped child processes are tied to VM lifetime.
  • Add a SubProcess::SetJobObject() API that wires PROC_THREAD_ATTRIBUTE_JOB_LIST into process creation.
  • Add a job object in DeviceHostProxy and assign the registered device host process to it.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/windows/service/exe/WslCoreVm.h Adds a VM-scoped job handle member to own the kill-on-close job lifetime.
src/windows/service/exe/WslCoreVm.cpp Creates/configures the job object and passes it to debug/relay/instance launches.
src/windows/service/exe/WslCoreInstance.h Extends instance construction to accept/store a job handle for subprocess launches.
src/windows/service/exe/WslCoreInstance.cpp Plumbs the job handle through to interop server launch.
src/windows/common/SubProcess.h Adds SetJobObject() API and stores an optional job handle.
src/windows/common/SubProcess.cpp Implements job attribute wiring via PROC_THREAD_ATTRIBUTE_JOB_LIST.
src/windows/common/helpers.hpp Extends Launch* helper signatures with an optional job handle parameter.
src/windows/common/helpers.cpp Plumbs job handle into SubProcess launches for wslhost/wslrelay.
src/windows/common/DeviceHostProxy.h Adds a job handle to manage device-host process lifetime.
src/windows/common/DeviceHostProxy.cpp Creates/configures the job and assigns the device host PID to it.

Comment thread src/windows/common/SubProcess.cpp
Comment thread src/windows/common/DeviceHostProxy.cpp
@benhillis benhillis force-pushed the benhill/job-object-child-processes branch from af651e7 to ad5556a Compare May 20, 2026 18:39
@benhillis benhillis marked this pull request as ready for review May 20, 2026 23:54
@benhillis benhillis requested a review from a team as a code owner May 20, 2026 23:54
Copilot AI review requested due to automatic review settings May 20, 2026 23:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

JohnMcPMS
JohnMcPMS previously approved these changes May 21, 2026
Copy link
Copy Markdown
Member

@JohnMcPMS JohnMcPMS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks reasonable, but I definitely don't know enough about the process graph to speak to its completeness.

Copilot AI review requested due to automatic review settings May 21, 2026 16:23
Comment on lines +268 to +273
m_processJobObject.reset(CreateJobObjectW(nullptr, nullptr));
THROW_LAST_ERROR_IF(!m_processJobObject);

JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo{};
jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
THROW_IF_WIN32_BOOL_FALSE(SetInformationJobObject(m_processJobObject.get(), JobObjectExtendedLimitInformation, &jobInfo, sizeof(jobInfo)));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the third instance of this code; probably worth a helper.

Ben Hillis and others added 4 commits May 21, 2026 09:29
Add JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE job objects to ensure that child
processes spawned by the service (wsldevicehost, wslhost.exe, wslrelay.exe)
are terminated when the VM shuts down. This prevents lingering processes
from holding DLLs locked during package upgrades.

Changes:
- DeviceHostProxy: Create a job object and assign the COM device host
  process to it in RegisterDeviceHost.
- WslCoreVm: Create a per-VM job object and pass it to LaunchDebugConsole,
  LaunchKdRelay, LaunchPortRelay, and WslCoreInstance.
- SubProcess: Add SetJobObject() which uses PROC_THREAD_ATTRIBUTE_JOB_LIST
  to assign the process to the job at creation time.
- helpers: Add optional JobObject parameter to Launch* functions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Follow-up to the per-VM job object work: WSLCVirtualMachine::LaunchPortRelay
spawns wslrelay.exe directly via SubProcess. Without a per-VM job object,
if wslcsession.exe crashes or is force-killed before signaling
m_vmTerminatingEvent, the spawned wslrelay.exe is orphaned and keeps
wslrelay.exe locked.

Create a JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE job in WSLCVirtualMachine::Initialize
and pass it to LaunchPortRelay via SubProcess::SetJobObject, matching the
pattern used in WslCoreVm.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member destruction order is reverse of declaration order. Declare
m_processJobObject before m_portRelayChannelRead/Write so the pipes
are destroyed first and the job object (which kills wslrelay.exe) is
destroyed last.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@benhillis benhillis force-pushed the benhill/job-object-child-processes branch from abbd937 to 5d87a5f Compare May 21, 2026 16:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Comment thread src/windows/common/SubProcess.cpp
Replace four near-identical CreateJobObject + SetInformationJobObject
blocks with a single helpers::CreateKillOnCloseJob() call:
  - WslCoreVm
  - DeviceHostProxy
  - WSLCVirtualMachine
  - WSLCSessionManager

Addresses code review feedback from JohnMcPMS.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@benhillis benhillis merged commit 35f6566 into master May 21, 2026
11 checks passed
@benhillis benhillis deleted the benhill/job-object-child-processes branch May 21, 2026 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants