job_windows.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package dockerclassic
  2. import (
  3. "fmt"
  4. "os"
  5. "syscall"
  6. "unsafe"
  7. )
  8. func init() {
  9. if err := killSubProcessesOnClose(); err != nil {
  10. fmt.Println("failed to create job:", err)
  11. }
  12. }
  13. var (
  14. kernel32 = syscall.NewLazyDLL("kernel32.dll")
  15. )
  16. type jobObjectExtendedLimitInformation struct {
  17. BasicLimitInformation struct {
  18. PerProcessUserTimeLimit uint64
  19. PerJobUserTimeLimit uint64
  20. LimitFlags uint32
  21. MinimumWorkingSetSize uintptr
  22. MaximumWorkingSetSize uintptr
  23. ActiveProcessLimit uint32
  24. Affinity uintptr
  25. PriorityClass uint32
  26. SchedulingClass uint32
  27. }
  28. IoInfo struct {
  29. ReadOperationCount uint64
  30. WriteOperationCount uint64
  31. OtherOperationCount uint64
  32. ReadTransferCount uint64
  33. WriteTransferCount uint64
  34. OtherTransferCount uint64
  35. }
  36. ProcessMemoryLimit uintptr
  37. JobMemoryLimit uintptr
  38. PeakProcessMemoryUsed uintptr
  39. PeakJobMemoryUsed uintptr
  40. }
  41. // killSubProcessesOnClose will ensure on windows that all child processes of the current process are killed if parent is killed.
  42. func killSubProcessesOnClose() error {
  43. job, err := createJobObject()
  44. if err != nil {
  45. return err
  46. }
  47. info := jobObjectExtendedLimitInformation{}
  48. info.BasicLimitInformation.LimitFlags = 0x2000
  49. if err := setInformationJobObject(job, info); err != nil {
  50. _ = syscall.CloseHandle(job)
  51. return err
  52. }
  53. proc, err := syscall.GetCurrentProcess()
  54. if err != nil {
  55. _ = syscall.CloseHandle(job)
  56. return err
  57. }
  58. if err := assignProcessToJobObject(job, proc); err != nil {
  59. _ = syscall.CloseHandle(job)
  60. return err
  61. }
  62. return nil
  63. }
  64. func createJobObject() (syscall.Handle, error) {
  65. res, _, err := kernel32.NewProc("CreateJobObjectW").Call(uintptr(unsafe.Pointer(nil)), uintptr(unsafe.Pointer(nil)))
  66. if res == 0 {
  67. return syscall.InvalidHandle, os.NewSyscallError("CreateJobObject", err)
  68. }
  69. return syscall.Handle(res), nil
  70. }
  71. func setInformationJobObject(job syscall.Handle, info jobObjectExtendedLimitInformation) error {
  72. infoClass := uint32(9)
  73. res, _, err := kernel32.NewProc("SetInformationJobObject").Call(uintptr(job), uintptr(infoClass), uintptr(unsafe.Pointer(&info)), uintptr(uint32(unsafe.Sizeof(info))))
  74. if res == 0 {
  75. return os.NewSyscallError("SetInformationJobObject", err)
  76. }
  77. return nil
  78. }
  79. func assignProcessToJobObject(job syscall.Handle, process syscall.Handle) error {
  80. res, _, err := kernel32.NewProc("AssignProcessToJobObject").Call(uintptr(job), uintptr(process))
  81. if res == 0 {
  82. return os.NewSyscallError("AssignProcessToJobObject", err)
  83. }
  84. return nil
  85. }