-
Notifications
You must be signed in to change notification settings - Fork 17.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/go: add a flag to use stdin as a cancellation side-channel #56163
Comments
Operating systems do allow for creating a child process in a way that it doesn't leave any orphan behind when killed. On Linux, the standard way is to call The fact that we can't do either of the above with |
That functionality is already available via |
How would this work with 'go run', which delegates its stdin/out/err to the target process? |
I see several problems with that approach:
We did use the platform-specific approach for file locking in the module cache, but there we didn't have a clean cross-platform alternative. Here we do. |
Ideally In the interim, on platforms that support |
This proposal has been added to the active column of the proposals project |
It's pretty common to call Can we use this with |
I suspect this thread was at least in part motivated by some of the cancellation problems we've seen in gopls, which uses go/packages, so I expect we would update that go/packages (actually its internal go-command helper package) to use the new mechanism.
The parent process would set exec.Cmd.Stdin to an open file descriptor (os.File) returned by, say, os.Pipe, and then close the other end of it in response to context cancellation. It might be useful for the flag documentation to link to an example of the correct code (in the absence of a standard package that would host it).
A test that defines its own main can do whatever it wants with stdin---a similar situation to 'go run'. |
Yes!
That's #50436, for which I am hoping to merge an implementation next week (assuming that the API revisions are approved). The configuration would ideally look like: cmd := exec.Command(…, "-stdin=keepalive", …)
stdin, err := cmd.StdinPipe()
if err != nil {
…
}
cmd.Cancel = func() error { return stdin.Close() } |
It seems like we should disallow go run -stdin=keepalive. |
Disallowing (I can think of reasonable ways one might use it, but use-cases are pretty niche and the implementation is simpler without it.) |
Does anyone object to making this change? |
I think this is a good idea. Perhaps a bit surprising to use this stdin mechanism directly, but I also don't imagine many people will need to do that themselves, instead relying on Out of curiosity, doesn't the same problem bubble up to programs like |
Based on the discussion above, this proposal seems like a likely accept. |
No change in consensus, so accepted. 🎉 |
Change https://go.dev/cl/456116 mentions this issue: |
Updates #50436. Updates #56163. Fixes #24050. Change-Id: I1b00eb8fb60e0879f029642b5bad97b2e139fee6 Reviewed-on: https://go-review.googlesource.com/c/go/+/456116 Reviewed-by: Ian Lance Taylor <iant@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Bryan Mills <bcmills@google.com> Auto-Submit: Bryan Mills <bcmills@google.com>
The
go
command executes a lot of subprocesses as it runs, including:vet
tool to analyze programs,go test
andgo test -fuzz=.
, andgo run
(proposal: cmd/go/internal/run: Use an exec without fork, or pass signals to child #54868 notwithstanding).Some clients of the
go
command end up starting a command, letting it run a while, and then wanting to cancel its execution. For example,gopls
may start runninggo list
to evaluate a program being edited, but then cancel that command due to another edit to the files in the user's workspace or a failure in some other concurrent processing that renders thego list
result irrelevant.If the
go
process itself is forcibly terminated usingos.Kill
(via either(*os.Process).Kill
orexec.CommandContext
), its subprocesses may continue to execute for an arbitrarily long time, leaking resources and (on Windows, at least) potentially preventing cleanup of temporary files and directories.On Unix platforms, we can instead request a clean shutdown using
os.Interrupt
. (Thego
command may or may not respond appropriately to that signal today, but in principle it can and that's what matters in terms of the public-facing API.) However,os.Interrupt
does not work on Windows, and per discussion on #6720 and #46345 we don't know of a general way to make it work. (At the very least, it would requireos/exec
to make invasive and likely-incompatible assumptions about consoles and process groups.)Since
os.Kill
leaks resources andos.Interrupt
isn't always available, we need some other way to request cancellation of a runninggo
command.Fortunately, we have a way out: as far as I am aware, every platform that supports
os/exec
in Go also supports inter-process pipes, andcmd/go
to my knowledge does not usestdin
for anything today.I propose that we add a new flag to the
go
command,-stdin=keepalive
, that will cause it to read from (and discard bytes from)stdin
until EOF, then make a best effort to cancel all subprocesses, clean up, and exit as soon as possible.The choice of
-stdin=keepalive
leaves open the possibility of usingstdin
in other ways in the future, while being clear about both its role (“keepalive”) and mechanism (“stdin”).(CC @golang/tools-team)
The text was updated successfully, but these errors were encountered: