diff --git a/src/process_win32.cpp b/src/process_win32.cpp index afe90ae..226fbb6 100644 --- a/src/process_win32.cpp +++ b/src/process_win32.cpp @@ -55,6 +55,27 @@ struct ProcessHandle CloseHandle(process_handle); } }; +// ============================================================================= +// Job Object for child process cleanup +// ============================================================================= + +// Singleton job object that kills all child processes when parent exits. +// Using JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE ensures spawned servers die +// when the client process terminates (even abnormally). +static HANDLE get_child_process_job() +{ + static HANDLE job = []() -> HANDLE { + HANDLE h = CreateJobObjectA(nullptr, nullptr); + if (h) + { + JOBOBJECT_EXTENDED_LIMIT_INFORMATION info = {}; + info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + SetInformationJobObject(h, JobObjectExtendedLimitInformation, &info, sizeof(info)); + } + return h; + }(); + return job; +} // ============================================================================= // Helper functions @@ -520,6 +541,11 @@ void Process::spawn( handle_->process_id = pi.dwProcessId; handle_->running = true; + // Assign to job object so child dies when parent dies + HANDLE job = get_child_process_job(); + if (job) + AssignProcessToJobObject(job, pi.hProcess); + stdin_->handle_->handle = stdin_write; stdout_->handle_->handle = stdout_read; stderr_->handle_->handle = stderr_read;