741fdcee24
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>
52 lines
1.2 KiB
Go
52 lines
1.2 KiB
Go
package infra
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
// ProcessWait waits for a subprocess to finish and collects its output.
|
|
// If timeoutSec > 0 and the process has not exited by then, ProcessKill is
|
|
// called with graceSec=5 and the result is marked Killed=true.
|
|
func ProcessWait(handle *ProcessHandle, timeoutSec int) (ProcessResult, error) {
|
|
// Wait for the process in a goroutine.
|
|
waitCh := make(chan error, 1)
|
|
go func() {
|
|
waitCh <- handle.Cmd.Wait()
|
|
}()
|
|
|
|
killed := false
|
|
|
|
if timeoutSec > 0 {
|
|
timer := time.NewTimer(time.Duration(timeoutSec) * time.Second)
|
|
defer timer.Stop()
|
|
select {
|
|
case <-timer.C:
|
|
// Timeout exceeded — kill the process group.
|
|
_ = ProcessKill(handle, 5)
|
|
killed = true
|
|
<-waitCh
|
|
case <-waitCh:
|
|
}
|
|
} else {
|
|
<-waitCh
|
|
}
|
|
|
|
// After Wait() returns, buffers are safe to read.
|
|
exitCode := 0
|
|
if handle.Cmd.ProcessState != nil {
|
|
exitCode = handle.Cmd.ProcessState.ExitCode()
|
|
} else if killed {
|
|
exitCode = -1
|
|
}
|
|
|
|
duration := time.Since(handle.StartTime)
|
|
|
|
return ProcessResult{
|
|
ExitCode: exitCode,
|
|
Stdout: handle.stdout.String(),
|
|
Stderr: handle.stderr.String(),
|
|
DurationMs: duration.Milliseconds(),
|
|
Killed: killed,
|
|
}, nil
|
|
}
|