//go:build linux package main import ( "os" "os/exec" "strconv" "strings" "syscall" ) // An engine runs in a process group of its own, so that stopping it stops everything it started (PyTorch workers). func setProcGroup(c *exec.Cmd) { c.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} } func signalGroup(pgid int, kill bool) { sig := syscall.SIGTERM if kill { sig = syscall.SIGKILL } syscall.Kill(-pgid, sig) } // pauseGroup stops or continues every process of an engine (to slow it down for a part of each second). func pauseGroup(pgid int, on bool) { sig := syscall.SIGCONT if on { sig = syscall.SIGSTOP } syscall.Kill(-pgid, sig) } // pgidOf is the process group of a process (0 if it cannot be read); field 5 of /proc//stat, after the "(name)". func pgidOf(pid int) int { b, err := os.ReadFile("/proc/" + strconv.Itoa(pid) + "/stat") if err != nil { return 0 } s := string(b) i := strings.LastIndexByte(s, ')') if i < 0 { return 0 } f := strings.Fields(s[i+1:]) // state ppid pgrp ... if len(f) < 3 { return 0 } g, _ := strconv.Atoi(f[2]) return g } func procCmdline(pid int) string { b, _ := os.ReadFile("/proc/" + strconv.Itoa(pid) + "/cmdline") return strings.ReplaceAll(string(b), "\x00", " ") }