Let's say I have a long-running function...
func longRunningThing(data string) {
for i := 0; i < 100; i++ {
fmt.Printf("%v %v\n", data, i)
time.Sleep(1 * time.Second)
}
}
... and I would like it to support context cancellation. Am I right to assume that it's the function's responsibility to periodically check if the passed in context has been cancelled?
For example, like this:
func longRunningThing(ctx context.Context, data string) {
for i := 0; i < 100; i++ {
select {
case <-ctx.Done():
return
default:
fmt.Printf("%v %v\n", data, i)
time.Sleep(1 * time.Second)
}
}
}
Reason I'm asking is, in the O’Reilly book Learning Go, there's a section Handling Context Cancellation in Your Own Code (in chapter 12) which suggests the following pattern:
func longRunningThingManager(ctx context.Context, data string) (string, error) {
type wrapper struct {
result string
error error
}
ch := make(chan wrapper, 1)
go func() {
result, err := longRunningThing(ctx, data)
ch <- wrapper{result, err}
}()
select {
case result := <-ch:
return result.result, result.error
case <-ctx.Done():
return "", ctx.Err()
}
}
The idea here appears to be that the longRunningThingManager
wrapper function keeps an eye on the context and instantly returns in the event of a cancellation, not longRunningThing
itself.
Here's where I'm confused: while that'll immediately unblock a blocked caller, I would assume that it doesn't actually stop the running goroutine. I.e. if longRunningThing
doesn't periodically check for context cancellation as well as outlined above, it'll actually keep running until the end, potentially wasting resources, causing side-effects etc.
Is my assumption correct, or am I missing something here?