Skip to content

Commit

Permalink
windows: set job object for executor and children
Browse files Browse the repository at this point in the history
On Windows, if the `raw_exec` driver's executor exits, the child processes are
not also killed. Create a Windows "job object" (not to be confused with a Nomad
job) and add the executor to it. Child processes of the executor will inherit
the job automatically. When the handle to the job object is freed (on executor
exit), the job itself is destroyed and this causes all processes in that job to
exit.

Fixes: #23668
Ref: https://learn.microsoft.com/en-us/windows/win32/procthread/job-objects
  • Loading branch information
tgross committed Oct 15, 2024
1 parent 61dd1f3 commit e4bd861
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .changelog/24214.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
windows: Fixed a bug where a crashed executor would orphan task processes
```
33 changes: 32 additions & 1 deletion drivers/shared/executor/executor_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,48 @@ import (
"fmt"
"os"
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

// configure new process group for child process
// configure new process group for child process and creates a JobObject for the
// executor. Children of the executor will be created in the same JobObject
// Ref: https://learn.microsoft.com/en-us/windows/win32/procthread/job-objects
func (e *UniversalExecutor) setNewProcessGroup() error {
// We need to check that as build flags includes windows for this file
if e.childCmd.SysProcAttr == nil {
e.childCmd.SysProcAttr = &syscall.SysProcAttr{}
}
e.childCmd.SysProcAttr.CreationFlags = syscall.CREATE_NEW_PROCESS_GROUP

// note: we don't call CloseHandle on this job handle because we need to
// hold onto it until the executor exits
job, err := windows.CreateJobObject(nil, nil)
if err != nil {
return fmt.Errorf("could not create Windows job object for executor: %w", err)
}

info := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{
BasicLimitInformation: windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{
LimitFlags: windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
},
}
_, err = windows.SetInformationJobObject(
job,
windows.JobObjectExtendedLimitInformation,
uintptr(unsafe.Pointer(&info)),
uint32(unsafe.Sizeof(info)))
if err != nil {
return fmt.Errorf("could not configure Windows job object for executor: %w", err)
}

handle := windows.CurrentProcess()
err = windows.AssignProcessToJobObject(job, handle)
if err != nil {
return fmt.Errorf("could not assign executor to Windows job object: %w", err)
}

return nil
}

Expand Down

0 comments on commit e4bd861

Please sign in to comment.