fix(ax): centralise pid lifecycle checks
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
6c246a7165
commit
eae4d3f904
8 changed files with 31 additions and 44 deletions
|
|
@ -4,7 +4,6 @@ package agentic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
|
|
@ -80,7 +79,7 @@ func (s *PrepSubsystem) DispatchSync(ctx context.Context, input DispatchSyncInpu
|
|||
case <-ctx.Done():
|
||||
return DispatchSyncResult{Error: "cancelled"}
|
||||
case <-ticker.C:
|
||||
if pid > 0 && syscall.Kill(pid, 0) != nil {
|
||||
if pid > 0 && !PIDAlive(pid) {
|
||||
// Process exited — read final status
|
||||
st, err := ReadStatus(wsDir)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -65,20 +65,20 @@ func (s *PrepSubsystem) gitOutput(ctx context.Context, dir string, args ...strin
|
|||
|
||||
// --- Process lifecycle helpers ---
|
||||
|
||||
// processIsRunning checks if a process is still alive via PID signal check.
|
||||
// PIDAlive checks if an OS process is still alive via PID signal check.
|
||||
//
|
||||
// if processIsRunning(st.ProcessID, st.PID) { ... }
|
||||
func processIsRunning(processID string, pid int) bool {
|
||||
// if agentic.PIDAlive(st.PID) { ... }
|
||||
func PIDAlive(pid int) bool {
|
||||
if pid > 0 {
|
||||
return syscall.Kill(pid, 0) == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// processKill terminates a process via SIGTERM.
|
||||
// PIDTerminate terminates a process via SIGTERM.
|
||||
//
|
||||
// processKill(st.ProcessID, st.PID)
|
||||
func processKill(processID string, pid int) bool {
|
||||
// if agentic.PIDTerminate(st.PID) { ... }
|
||||
func PIDTerminate(pid int) bool {
|
||||
if pid > 0 {
|
||||
return syscall.Kill(pid, syscall.SIGTERM) == nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,38 +179,38 @@ func TestProc_GitOutput_Ugly(t *testing.T) {
|
|||
assert.Equal(t, "", out)
|
||||
}
|
||||
|
||||
// --- processIsRunning ---
|
||||
// --- PIDAlive ---
|
||||
|
||||
func TestProc_ProcessIsRunning_Good(t *testing.T) {
|
||||
func TestProc_PIDAlive_Good(t *testing.T) {
|
||||
// Own PID should be running
|
||||
pid, _ := strconv.Atoi(core.Env("PID"))
|
||||
assert.True(t, processIsRunning("", pid))
|
||||
assert.True(t, PIDAlive(pid))
|
||||
}
|
||||
|
||||
func TestProc_ProcessIsRunning_Bad(t *testing.T) {
|
||||
func TestProc_PIDAlive_Bad(t *testing.T) {
|
||||
// PID 999999 should not be running (extremely unlikely to exist)
|
||||
assert.False(t, processIsRunning("", 999999))
|
||||
assert.False(t, PIDAlive(999999))
|
||||
}
|
||||
|
||||
func TestProc_ProcessIsRunning_Ugly(t *testing.T) {
|
||||
func TestProc_PIDAlive_Ugly(t *testing.T) {
|
||||
// PID 0 — should return false (invalid PID guard: pid > 0 is false for 0)
|
||||
assert.False(t, processIsRunning("", 0))
|
||||
assert.False(t, PIDAlive(0))
|
||||
}
|
||||
|
||||
// --- processKill ---
|
||||
// --- PIDTerminate ---
|
||||
|
||||
func TestProc_ProcessKill_Good(t *testing.T) {
|
||||
func TestProc_PIDTerminate_Good(t *testing.T) {
|
||||
t.Skip("would need real process to kill")
|
||||
}
|
||||
|
||||
func TestProc_ProcessKill_Bad(t *testing.T) {
|
||||
func TestProc_PIDTerminate_Bad(t *testing.T) {
|
||||
// PID 999999 should fail to kill
|
||||
assert.False(t, processKill("", 999999))
|
||||
assert.False(t, PIDTerminate(999999))
|
||||
}
|
||||
|
||||
func TestProc_ProcessKill_Ugly(t *testing.T) {
|
||||
func TestProc_PIDTerminate_Ugly(t *testing.T) {
|
||||
// PID 0 — pid > 0 guard returns false
|
||||
assert.False(t, processKill("", 0))
|
||||
assert.False(t, PIDTerminate(0))
|
||||
}
|
||||
|
||||
// --- initTestRepo creates a git repo with commits for proc tests ---
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package agentic
|
|||
|
||||
import (
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
|
|
@ -167,10 +166,8 @@ func (s *PrepSubsystem) countRunningByAgent(agent string) int {
|
|||
if s.workspaces != nil && s.workspaces.Len() > 0 {
|
||||
count := 0
|
||||
s.workspaces.Each(func(_ string, st *WorkspaceStatus) {
|
||||
if st.Status == "running" && baseAgent(st.Agent) == agent {
|
||||
if st.PID > 0 && syscall.Kill(st.PID, 0) == nil {
|
||||
count++
|
||||
}
|
||||
if st.Status == "running" && baseAgent(st.Agent) == agent && PIDAlive(st.PID) {
|
||||
count++
|
||||
}
|
||||
})
|
||||
return count
|
||||
|
|
@ -192,7 +189,7 @@ func (s *PrepSubsystem) countRunningByAgentDisk(agent string) int {
|
|||
if baseAgent(st.Agent) != agent {
|
||||
continue
|
||||
}
|
||||
if st.PID > 0 && syscall.Kill(st.PID, 0) == nil {
|
||||
if PIDAlive(st.PID) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
|
@ -207,10 +204,8 @@ func (s *PrepSubsystem) countRunningByModel(agent string) int {
|
|||
if s.workspaces != nil && s.workspaces.Len() > 0 {
|
||||
count := 0
|
||||
s.workspaces.Each(func(_ string, st *WorkspaceStatus) {
|
||||
if st.Status == "running" && st.Agent == agent {
|
||||
if st.PID > 0 && syscall.Kill(st.PID, 0) == nil {
|
||||
count++
|
||||
}
|
||||
if st.Status == "running" && st.Agent == agent && PIDAlive(st.PID) {
|
||||
count++
|
||||
}
|
||||
})
|
||||
return count
|
||||
|
|
@ -226,7 +221,7 @@ func (s *PrepSubsystem) countRunningByModel(agent string) int {
|
|||
if st.Agent != agent {
|
||||
continue
|
||||
}
|
||||
if st.PID > 0 && syscall.Kill(st.PID, 0) == nil {
|
||||
if PIDAlive(st.PID) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package agentic
|
|||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
|
|
@ -142,7 +141,7 @@ func (s *PrepSubsystem) status(ctx context.Context, _ *mcp.CallToolRequest, inpu
|
|||
|
||||
// If status is "running", check if PID is still alive
|
||||
if st.Status == "running" && st.PID > 0 {
|
||||
if err := syscall.Kill(st.PID, 0); err != nil {
|
||||
if !PIDAlive(st.PID) {
|
||||
blockedPath := workspaceBlockedPath(wsDir)
|
||||
if r := fs.Read(blockedPath); r.OK {
|
||||
st.Status = "blocked"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ package monitor
|
|||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
|
|
@ -297,10 +296,7 @@ func (m *Subsystem) countLiveWorkspaces() (running, queued int) {
|
|||
|
||||
// pidAlive checks whether a process is still running.
|
||||
func pidAlive(pid int) bool {
|
||||
if pid <= 0 {
|
||||
return false
|
||||
}
|
||||
return syscall.Kill(pid, 0) == nil
|
||||
return agentic.PIDAlive(pid)
|
||||
}
|
||||
|
||||
func (m *Subsystem) loop(ctx context.Context) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ package runner
|
|||
|
||||
import (
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
|
|
@ -170,7 +169,7 @@ func (s *Service) countRunningByAgent(agent string) int {
|
|||
switch {
|
||||
case st.PID < 0:
|
||||
count++
|
||||
case st.PID > 0 && syscall.Kill(st.PID, 0) == nil:
|
||||
case st.PID > 0 && agentic.PIDAlive(st.PID):
|
||||
count++
|
||||
}
|
||||
})
|
||||
|
|
@ -189,7 +188,7 @@ func (s *Service) countRunningByModel(agent string) int {
|
|||
switch {
|
||||
case st.PID < 0:
|
||||
count++
|
||||
case st.PID > 0 && syscall.Kill(st.PID, 0) == nil:
|
||||
case st.PID > 0 && agentic.PIDAlive(st.PID):
|
||||
count++
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ package runner
|
|||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"dappco.re/go/agent/pkg/agentic"
|
||||
|
|
@ -338,7 +337,7 @@ func (s *Service) actionKill(_ context.Context, _ core.Options) core.Result {
|
|||
killed := 0
|
||||
s.workspaces.Each(func(_ string, st *WorkspaceStatus) {
|
||||
if st.Status == "running" && st.PID > 0 {
|
||||
if syscall.Kill(st.PID, syscall.SIGTERM) == nil {
|
||||
if agentic.PIDTerminate(st.PID) {
|
||||
killed++
|
||||
}
|
||||
st.Status = "failed"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue