I want to utilize context in golang to be used for cancellation when timeout reached.
The code:
package main
import "fmt"
import "time"
import "context"
func F(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx,3*time.Second)
defer cancel()
for i:=0;i<10;i++ {
time.Sleep(1 * time.Second)
fmt.Println("No: ",i)
}
select {
case <-ctx.Done():
fmt.Println("TIME OUT")
cancel()
return ctx.Err()
default:
fmt.Println("ALL DONE")
return nil
}
}
func main() {
ctx := context.Background()
err := F(ctx)
if err != nil {
fmt.Println(err)
}else {
fmt.Println("Success")
}
}
Expectation:
code above should stop running the loop at counter 2, because the timeout is 3 second and looping run 1 second each. So I expect someting like this:
No: 0
No: 1
No: 2
TIME OUT
context deadline exceeded
Actual:
What actually happen is the loop keep running until finish even though the context meet timeout and the select listener catch that on <-ctx.Done(). This code prints this:
No: 0
No: 1
No: 2
No: 3
No: 4
No: 5
No: 6
No: 7
No: 8
No: 9
TIME OUT
context deadline exceeded
How to stop the function execution after timeout meet?
context.Context can only relay the message that timeout or cancellation happened. It does not have the power to actually stop any goroutines (for details, see cancel a blocking operation in Go). The goroutine itself is responsible for checking the timeout and cancellation, and abort early.
You have a loop which unconditionally iterates 10 times and prints something. And you only check the timeout after the loop.
You have to move the context checking into the loop:
func F(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
for i := 0; i < 10; i++ {
select {
case <-ctx.Done():
fmt.Println("TIME OUT")
return ctx.Err()
default:
time.Sleep(1 * time.Second)
fmt.Println("No: ", i)
}
}
fmt.Println("ALL DONE")
return nil
}
With this change, output will be (try it on the Go Playground):
No: 0
No: 1
No: 2
No: 3
TIME OUT
context deadline exceeded
Note: whether you see "No: 3" printed may or may not happen, as any iteration takes 1 second, and timeout is 3 seconds = 3 * iteration delay, so whether timeout happens first or the 4th iteration begins first is "racy". If you decrease the timeout to like 2900 ms, "No: 3" will not be printed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With