diff --git a/.changelog/24214.txt b/.changelog/24214.txt new file mode 100644 index 00000000000..d0e59532db9 --- /dev/null +++ b/.changelog/24214.txt @@ -0,0 +1,3 @@ +```release-note:bug +windows: Fixed a bug where a crashed executor would orphan task processes +``` diff --git a/drivers/shared/executor/executor_windows.go b/drivers/shared/executor/executor_windows.go index 457f29a6e02..25134ece5d1 100644 --- a/drivers/shared/executor/executor_windows.go +++ b/drivers/shared/executor/executor_windows.go @@ -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 }