feat: improve closing resources (PTYs, connections) (#355)

This commit is contained in:
Carlos Florêncio
2026-03-03 19:54:12 +00:00
committed by GitHub
parent c191ef2577
commit 43eccfaee2
5 changed files with 24 additions and 7 deletions
@@ -90,12 +90,14 @@ func (m *Manager) StopAgentWithReason(ctx context.Context, executionID string, r
zap.Bool("force", force),
zap.String("runtime", execution.RuntimeName))
// Try to gracefully stop via agentctl first
if execution.agentctl != nil && !force {
if err := execution.agentctl.Stop(ctx); err != nil {
m.logger.Warn("failed to stop agent via agentctl",
zap.String("execution_id", executionID),
zap.Error(err))
// Try to gracefully stop via agentctl first, then always close connections
if execution.agentctl != nil {
if !force {
if err := execution.agentctl.Stop(ctx); err != nil {
m.logger.Warn("failed to stop agent via agentctl",
zap.String("execution_id", executionID),
zap.Error(err))
}
}
execution.agentctl.Close()
}
@@ -532,10 +532,11 @@ type (
ProcessStatusUpdate = types.ProcessStatusUpdate
)
// Close closes all connections
// Close closes all connections and releases resources
func (c *Client) Close() {
c.CloseUpdatesStream()
c.CloseWorkspaceStream()
c.httpClient.CloseIdleConnections()
}
// readResponseBody reads and returns the response body
@@ -412,10 +412,13 @@ func (m *Manager) startProcessPipes() error {
}
m.stdout, err = m.cmd.StdoutPipe()
if err != nil {
_ = m.stdin.Close()
return fmt.Errorf("failed to create stdout pipe: %w", err)
}
m.stderr, err = m.cmd.StderrPipe()
if err != nil {
_ = m.stdin.Close()
_ = m.stdout.Close()
return fmt.Errorf("failed to create stderr pipe: %w", err)
}
return nil
@@ -245,6 +245,7 @@ func (r *ProcessRunner) Start(ctx context.Context, req StartProcessRequest) (*Pr
}
stderr, err := cmd.StderrPipe()
if err != nil {
_ = stdout.Close()
return nil, fmt.Errorf("failed to attach stderr: %w", err)
}
@@ -296,6 +297,8 @@ func (r *ProcessRunner) Start(ctx context.Context, req StartProcessRequest) (*Pr
// On failure, marks the process as failed and removes it from tracking.
func (r *ProcessRunner) startAndActivate(proc *commandProcess, cmd *exec.Cmd, id string, stdout, stderr io.ReadCloser) error {
if err := cmd.Start(); err != nil {
_ = stdout.Close()
_ = stderr.Close()
proc.mu.Lock()
proc.info.Status = types.ProcessStatusFailed
proc.info.UpdatedAt = time.Now().UTC()
@@ -379,6 +379,14 @@ func (s *Session) respawn() error {
return nil
}
// Close old PTY to prevent file descriptor leak.
// Don't nil out s.pty — readOutput() may still be reading from it
// concurrently without the mutex. Closing is sufficient: any in-flight
// Read() will return an error and the goroutine will exit.
if s.pty != nil {
_ = s.pty.Close()
}
s.cmd = exec.Command(s.shell, s.shellArgs...)
s.cmd.Dir = s.workDir
s.cmd.Env = buildShellEnv(s.workDir)