-
Notifications
You must be signed in to change notification settings - Fork 18.3k
Description
I needed timeout functionality for one of my projects, so I looked in the time
package. My timeouts where fallbacks in case a channel receive took too long. Most of the time the channel would receive before the timeout and I wanted to release the timeout resources when they where no longer needed. Documentation for time.After() says:
[...] If efficiency is a concern, use NewTimer instead and call Timer.Stop if the timer is no longer needed.
So I used a time.Timer and according to the documentation for time.Timer.Stop() one should drain the channel if time.Timer.Stop()
returns false:
if !t.Stop() {
<-t.C
}
I later discovered that my threads got stuck on receive like in this playground example when timer where triggered before I called stop:
t := time.NewTimer(time.Second * 3)
defer func() {
if !t.Stop() {
<-t.C
}
}()
<-t.C
Wrapping the drain in a select seems to do the trick:
t := time.NewTimer(time.Second * 3)
defer func() {
t.Stop()
select {
case <-t.C:
default:
}
}()
<-t.C
Documentation should make it clear how to safely drain the channel.