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,51 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user