Windows specific : ensure all child processes are killed when parent exits (also works when killing parent). From Desktop code

This commit is contained in:
guillaume.tardif 2020-06-04 16:32:21 +02:00 committed by Guillaume Tardif
parent 7c6b04f28e
commit 995d047608
1 changed files with 93 additions and 0 deletions

View File

@ -0,0 +1,93 @@
package dockerclassic
import (
"fmt"
"os"
"syscall"
"unsafe"
)
func init() {
if err := killSubProcessesOnClose(); err != nil {
fmt.Println("failed to create job:", err)
}
}
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
)
type jobObjectExtendedLimitInformation struct {
BasicLimitInformation struct {
PerProcessUserTimeLimit uint64
PerJobUserTimeLimit uint64
LimitFlags uint32
MinimumWorkingSetSize uintptr
MaximumWorkingSetSize uintptr
ActiveProcessLimit uint32
Affinity uintptr
PriorityClass uint32
SchedulingClass uint32
}
IoInfo struct {
ReadOperationCount uint64
WriteOperationCount uint64
OtherOperationCount uint64
ReadTransferCount uint64
WriteTransferCount uint64
OtherTransferCount uint64
}
ProcessMemoryLimit uintptr
JobMemoryLimit uintptr
PeakProcessMemoryUsed uintptr
PeakJobMemoryUsed uintptr
}
// killSubProcessesOnClose will ensure on windows that all child processes of the current process are killed if parent is killed.
func killSubProcessesOnClose() error {
job, err := createJobObject()
if err != nil {
return err
}
info := jobObjectExtendedLimitInformation{}
info.BasicLimitInformation.LimitFlags = 0x2000
if err := setInformationJobObject(job, info); err != nil {
_ = syscall.CloseHandle(job)
return err
}
proc, err := syscall.GetCurrentProcess()
if err != nil {
_ = syscall.CloseHandle(job)
return err
}
if err := assignProcessToJobObject(job, proc); err != nil {
_ = syscall.CloseHandle(job)
return err
}
return nil
}
func createJobObject() (syscall.Handle, error) {
res, _, err := kernel32.NewProc("CreateJobObjectW").Call(uintptr(unsafe.Pointer(nil)), uintptr(unsafe.Pointer(nil)))
if res == 0 {
return syscall.InvalidHandle, os.NewSyscallError("CreateJobObject", err)
}
return syscall.Handle(res), nil
}
func setInformationJobObject(job syscall.Handle, info jobObjectExtendedLimitInformation) error {
infoClass := uint32(9)
res, _, err := kernel32.NewProc("SetInformationJobObject").Call(uintptr(job), uintptr(infoClass), uintptr(unsafe.Pointer(&info)), uintptr(uint32(unsafe.Sizeof(info))))
if res == 0 {
return os.NewSyscallError("SetInformationJobObject", err)
}
return nil
}
func assignProcessToJobObject(job syscall.Handle, process syscall.Handle) error {
res, _, err := kernel32.NewProc("AssignProcessToJobObject").Call(uintptr(job), uintptr(process))
if res == 0 {
return os.NewSyscallError("AssignProcessToJobObject", err)
}
return nil
}