What is context.AfterFunc and what are the semantics of its stop function?
Question 273HardGo 1.22 to 1.25
func AfterFunc(ctx Context, f func()) (stop func() bool) (Go 1.21) arranges for f to run in its own goroutine once ctx is done. If ctx is already done, f starts right away. For stdlib contexts this avoids keeping a watcher goroutine parked on <-ctx.Done(): registration is just an entry in the parent's children map.
stop() removes the registration:
- It returns true if this call prevented
ffrom running. - It returns false if
fhas already been started orstopwas already called. - It does not wait for a running
fto finish. If you need that, synchronize yourself.
// Wake a sync.Cond waiter when ctx is canceled
func waitCond(ctx context.Context, mu *sync.Mutex, cond *sync.Cond, ready func() bool) error {
stop := context.AfterFunc(ctx, func() {
mu.Lock()
defer mu.Unlock()
cond.Broadcast()
})
defer stop()
mu.Lock()
defer mu.Unlock()
for !ready() {
if err := ctx.Err(); err != nil {
return err
}
cond.Wait()
}
return nil
}
Typical uses: unblocking APIs that don't accept a context (condition variables, net.Conn deadlines), merging contexts, and cleanup hooks. Calling AfterFunc several times on the same context registers several independent callbacks.
More on Context
- Q271Why is storing a context.Context in a struct considered an anti-pattern? Are there exceptions?
- Q272context.Background() vs context.TODO(): when do you use each, and why not pass nil?
- Q274How do you make a blocking call that doesn't accept a context (e.g. net.Conn.Read) cancelable?
- Q275What does context.WithoutCancel do and when would you use it?
- Q276What is wrong with this handler? What error will the goroutine see?
- Q277When exactly is an http.Request's context canceled on the server side, and how do you use it correctly?