feat: improve closing resources (PTYs, connections) (#355)
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user