feat: add process manager and execution store types (0007b, 0007c)
Process spawn/wait/kill functions for subprocess management with output capture, timeout, and process group cleanup. DagRun and DagStepResult types for SQLite execution persistence. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ProcessKill sends SIGTERM to the process group of handle, then waits up to
|
||||
// graceSec seconds for the process to exit. If it is still alive after the
|
||||
// grace period, SIGKILL is sent. Returns an error only if the signal could not
|
||||
// be delivered (e.g. the process group does not exist).
|
||||
func ProcessKill(handle *ProcessHandle, graceSec int) error {
|
||||
// Send SIGTERM to the process group (negative pid targets the group).
|
||||
if err := syscall.Kill(-handle.Pid, syscall.SIGTERM); err != nil {
|
||||
// ESRCH means the process is already gone — not an error from our view.
|
||||
if err != syscall.ESRCH {
|
||||
return fmt.Errorf("process_kill: sigterm: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Poll until the process exits or the grace period expires.
|
||||
deadline := time.Now().Add(time.Duration(graceSec) * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
// Check if process has exited by sending signal 0 (no-op).
|
||||
err := syscall.Kill(-handle.Pid, 0)
|
||||
if err == syscall.ESRCH {
|
||||
// Process group is gone.
|
||||
return nil
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Still alive after grace period — escalate to SIGKILL.
|
||||
if err := syscall.Kill(-handle.Pid, syscall.SIGKILL); err != nil {
|
||||
if err != syscall.ESRCH {
|
||||
return fmt.Errorf("process_kill: sigkill: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user