Non-blocking observation of a context cancellation or process cancellation?

127 views
Skip to first unread message

Peter Bočan

unread,
Aug 13, 2025, 1:59:37 PMAug 13
to golang-nuts
Hello all, 

I am running into a little nifty problem. I have a program which can run an arbitrary command, and in a separate goroutine it awaits the termination of the subprocess. This works nicely, but not as nicely as I would want to. If my program terminates, the Wait() will error out with "signal: killed", which is fine, if it's intended to be terminated alongside with my program. However, if that's not the case and the subprocess is terminated for other reasons, the observing the channel as outlined below blocks the goroutine indefinitely, which is not something I want. 
       
        c := exec.CommandContext(ctx, ...)
        c.Start()
go func() {
log.Debug().Msg("waiting for a program...")
err := c.Wait()
                select {
                    case <- ctx.Done(): 
                        // it's ever done, if the process terminates early
                }
if err != nil {
                        // Prints out that the program received the signal, which is not something I want to do.
log.Fatal().Err(err).Msg("failed to run the program") 
}
}()

I could get around it other ways (using WaitGroup), but do you think there is a slightly nicer way, if we could include a method on the Context interface called "Cancelled() bool" which would simply return a boolean value if the context was somewhere somehow cancelled? Or maybe it should be done on the exec.Cmd object itself? 

        c := exec.CommandContext(ctx, ...)
c.Start() 
        go func() {
log.Debug().Msg("waiting for a program...")
// no more Wait() necessary.
                select {
                    case <- c.Done(): 
                        // done, if the process terminates early
                    case <- c.Cancelled()
                        // done, if the execution context has been cancelled
                 }
}()

Just a food for thought, 
Peter Bocan.

burak serdar

unread,
Aug 13, 2025, 4:03:57 PMAug 13
to Peter Bočan, golang-nuts
c.Wait() already waits for the command to exit, so the following should work:

err:=c.Wait()
if ctx.Err()!=nil {
// Context timeout
} else {
// Something else
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/golang-nuts/61e32341-38b4-4e9b-87d1-f093001440den%40googlegroups.com.

Jason E. Aten

unread,
Aug 13, 2025, 6:13:15 PMAug 13
to golang-nuts
Hi Peter,

You might find the Wait4 idea useful; here for example is what I use to monitor (and restart)
dependent processes


See in particular the syscall to Wait4 here


Best,
Jason

Brian Candler

unread,
Aug 15, 2025, 12:45:03 AMAug 15
to golang-nuts
On Wednesday, 13 August 2025 at 23:59:37 UTC+6 Peter Bočan wrote:
if we could include a method on the Context interface called "Cancelled() bool" which would simply return a boolean value if the context was somewhere somehow cancelled?

Not saying that it helps in this case, but I think context.Err() gives you what you asked for.

// If Done is not yet closed, Err returns nil.
// If Done is closed, Err returns a non-nil error explaining why:
// DeadlineExceeded if the context's deadline passed,
// or Canceled if the context was canceled for some other reason.
// After Err returns a non-nil error, successive calls to Err return the same error.
Err() error
Reply all
Reply to author
Forward
0 new messages