Description
Hello up there.
Please find below description of real-world case where third party context needs to create its own done channel. @rsc was asking for example of such cases and I already commented on the subject on #28728 (comment), but it looks that feedback was lost due to that #28728 was already closed. Please either reopen #28728 or consider my case here. Thank you.
---- 8< ----
@rsc wrote at #28728 (comment):
There will still be a goroutine if WithCancel/WithDeadline are passed a parent context implementation that allocates its own done channels. I don't think that's particularly common, but of course I'd love to hear otherwise.
@rsc wrote at #28728 (comment):
are there any real-world examples of code that would still allocate goroutines in WithCancel etc?
I'm a bit late here, but below is example of real-world usage where custom context needs to create its own done channel: this need arises when one wants to merge two contexts to cancel the work when either the service is stopped, or when caller's context is canceled. Let me quote https://godoc.org/lab.nexedi.com/kirr/go123/xcontext#hdr-Merging_contexts to explain:
---- 8< ----
Merging contexts
Merge could be handy in situations where spawned job needs to be canceled whenever any of 2 contexts becomes done. This frequently arises with service methods that accept context as argument, and the service itself, on another control line, could be instructed to become non-operational. For example:
func (srv *Service) DoSomething(ctx context.Context) (err error) {
defer xerr.Contextf(&err, "%s: do something", srv)
// srv.serveCtx is context that becomes canceled when srv is
// instructed to stop providing service.
origCtx := ctx
ctx, cancel := xcontext.Merge(ctx, srv.serveCtx)
defer cancel()
err = srv.doJob(ctx)
if err != nil {
if ctx.Err() != nil && origCtx.Err() == nil {
// error due to service shutdown
err = ErrServiceDown
}
return err
}
...
}
func Merge
func Merge(parent1, parent2 context.Context) (context.Context, context.CancelFunc)
Merge merges 2 contexts into 1.
The result context:
- is done when parent1 or parent2 is done, or cancel called, whichever happens first,
- has deadline = min(parent1.Deadline, parent2.Deadline),
- has associated values merged from parent1 and parent2, with parent1 taking precedence.
Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete.
---- 8< ----
To do the merging of ctx
and srv.serveCtx
done channels current implementation has to allocate its own done channel and spawn corresponding goroutine:
https://lab.nexedi.com/kirr/go123/blob/5667f43e/xcontext/xcontext.go#L90-118
https://lab.nexedi.com/kirr/go123/blob/5667f43e/xcontext/xcontext.go#L135-150
context.WithCancel
, when called on resulting merged context, will have to spawn its own propagation goroutine too.
For the reference here is context.Merge
implementation in Pygolang that does parents - child binding via just data:
https://lab.nexedi.com/kirr/pygolang/blob/64765688/golang/context.cpp#L74-76
https://lab.nexedi.com/kirr/pygolang/blob/64765688/golang/context.cpp#L347-352
https://lab.nexedi.com/kirr/pygolang/blob/64765688/golang/context.cpp#L247-251
https://lab.nexedi.com/kirr/pygolang/blob/64765688/golang/context.cpp#L196-226
Appologize, once again, for being late here, and thanks beforehand for taking my example into account.
Kirill
/cc @Sajmani