Skip to content

os/exec: CommandContext should also cause the stdout/stderr pipes to be closed #21922

Closed
@nhooyr

Description

@nhooyr

I'm trying to run a process and kill it if it takes too long. So I create a command with a timeout context. Then, when the context expires and the process is not done, it will be killed. This approach works fine unless my Go process is reading from the process's stdout/stderr and the process forks and gives its stdout to its child. Then, cmd.Wait() will not exit until the child is done writing to stdout. I think that is unexpected because once the context expires, cmd.Wait() should unblock.

Here is a minimal example that reproduces my issue:

package main

import (
	"bytes"
	"context"
	"log"
	"os/exec"
	"time"
)

func main() {
	log.SetFlags(0)

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)

	cmd := exec.CommandContext(ctx, "bash", "-c", "echo -n foo; sleep 5")
	stdout := &bytes.Buffer{}
	cmd.Stdout = stdout

	start := time.Now()

	err := cmd.Start()
	must(err)

	err = cmd.Wait()
	cancel()

	log.Println("took:", time.Since(start))
	log.Println("expected it to take:", time.Second)
	log.Println("stdout:", stdout)
	log.Println("wait error:", err)
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

So what I'm proposing is that the stdout/stderr pipes should be closed once the process dies and the context has expired. This will allow the copying goroutines to exit and cmd.Wait() to unblock after the context expires as expected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions