Hi, I just thought about an example, where storing a Context in a struct actually does make sense:
Imagine a NewXXX function, which takes a ctx and returns some struct, lets call it TaskRunner.
It also launches a goroutine in the background, which is necessary for the struct to operate (maybe a task poller or whatever).
The struct also has a method, lets call it offerTask(localCtx, ... task), which blocks, while the task is processed.
Due to blocking, it also takes a context.
Now, a running and blocking offerTask should return, when
- localCtx is cancelled
- the ctx passed to NewXXX is cancelled (because the background goroutine is obviously not able to accept tasks after cancelling).
- the task succeeds.
The Select should look like this:
select {
case <-localCtx.Done():
// clean up
return
case <-TaskRunner.ctx.Done():
// clean up
return
case <-taskResultChan:
fetch result...
}
Since the TaskRunner.ctx is local to NewXXX, it cannot be used in offerTask, without storing it in a struct, but this use case does not have any of your cons, since it also takes a localCtx.
In my opinion, this is a perfectly valid and suitable use case for storing ctx in structs. (As long you make sure to select on all ctx's).
What do you think about this ?
Thanks in advance for this interesting discussion =).
Regards,
Chris
PS:
If you are interested:
My use case is a token bucket, with a goroutine running and generating tokens.
I want to shutdown the token bucket, when a specific ctx is cancelled NewTokenBucket(ctx...).
There is a method called Take(ctx), which tries to take tokens and blocks if no tokens are available.
Now, Take(...) should unblock, if the bucket is cancelled, because it would otherwise block forever.
Right now, I do this with a manual done channel, but I think its wrong to duplicate exit strategies.