When I add a defer in a function I expect that it will be always called when the function ends. I noticed that it does not happen when the function is times out.
package main
import (
"context"
"fmt"
"time"
)
func service1(ctx context.Context, r *Registry) {
ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
defer func() {
r.Unset("service 1")
}()
r.Set("service 1")
go service2(ctx, r)
select {
case <-ctx.Done():
cancel()
break
}
}
func service2(ctx context.Context, r *Registry) {
defer func() {
r.Unset("service 2")
}()
r.Set("service 2")
time.Sleep(time.Millisecond * 300)
}
type Registry struct {
entries map[string]bool
}
func (r *Registry)Set(key string) {
r.entries[key] = true
}
func (r *Registry)Unset(key string) {
r.entries[key] = false
}
func (r *Registry)Print() {
for key, val := range r.entries {
fmt.Printf("%s -> %v
", key, val)
}
}
func NewRegistry() *Registry {
r := Registry{}
r.entries = make(map[string]bool)
return &r
}
func main() {
r := NewRegistry()
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*200)
go service1(ctx, r)
// go service3(ctx, r)
select {
case <-ctx.Done():
fmt.Printf("context err: %s
", ctx.Err())
cancel()
}
r.Print()
}
In the example above, the defer in service2()
is never called and that's why the output is:
service 1 -> false
service 2 -> true
instead of
service 1 -> false
service 2 -> false
I understand that timeout means "stop executing" but it's reasonable to me execute deferred code. I could not find any explanation of this behavior.
And the second part of the question - how to modify the service or Registry
to be resistant to such situations?