+ Using select statement:
func retimer(t *time.Timer, d time.Duration) {
if t.Reset(d) {return}select {case <-t.C:default:}}
+ Using len function and chan receive:
func untimer(t *time.Timer) {if !t.Stop() && len(t.C) != 0 {<-t.C}}
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
After t.Stop(), the channel will not trigger again. t.C is an unbuffered channel, so len(t.C) is always 0. After t.Reset(d) the timer will not trigger until after duration d. Your two functions above can be replaced with t.Reset(d) and t.Stop() with no change in behavior.
On Wed, Jul 1, 2015 at 8:53 AM enormouspenguin <kimkh...@gmail.com> wrote:In order to make sure that no leftover/garbage Time value linger inside Timer.C channel after calling Timer.Reset or Timer.Stop, possibly causing unexpected notification, is any of the following snippets the correct way to handle that:+ Using select statement:func retimer(t *time.Timer, d time.Duration) {
if t.Reset(d) {return}select {case <-t.C:default:}}
+ Using len function and chan receive:func untimer(t *time.Timer) {if !t.Stop() && len(t.C) != 0 {<-t.C}}
An alternative would be to always Stop() before reset, and if Stop returns false, do a non-blocking receive from C before Reset. That way you know Reset will return false and any subsequent receive from C will be after the new delay. You won't have to track whether you had received from C.
Well, I think of Reset as just a way to push out a time out, for instance when you want to timeout on reading from the network, but whenever you get some more data you want to restart the timer. This use case doesn't really need the hoops mentioned. In other cases it's probably cleaner just to create a new timer.
I do think the following could be a much cleaner API:
// AfterChan waits for the duration to elapse and then sends the current time on the channel.
func AfterChan(d Duration, c chan<- Time) {// equivalent to the following but implemented more efficientlyAfterFunc(d, func(){select { case c <- Now(): ; default: }})}If you decide to reset you could just call AfterChan again with a new channel and ignore the old channel.
--
I hope we can fix this before Go 1.5.
--
You could keep track of the last time you reset, and check the time.Time that's returned from the channel; if it's not after the last reset time + delay, throw it away. http://play.golang.org/p/26M3C9S3RA
var timerPool sync.Pool
func acquireTimer(timeout time.Duration) *time.Timer {
tv := timerPool.Get()
if tv == nil {
return time.NewTimer(timeout)
}
t := tv.(*time.Timer)
if t.Reset(timeout) {
panic("BUG: Active timer trapped into acquireTimer()")
}
return t
}
func releaseTimer(t *time.Timer) {
if !t.Stop() {
// Collect possibly added time from the channel
// if timer has been stopped and nobody collected its' value.
select {
case <-t.C:
default:
}
}
timerPool.Put(t)
}