Don't cause rare spurious event timeout

Correctly resetting timers is surprisingly tricky.
This commit is contained in:
Jakob Borg
2015-11-17 12:03:18 +01:00
parent dcb773e446
commit 0167b4b4a3

View File

@@ -164,11 +164,20 @@ func (l *Logger) Log(t EventType, data interface{}) {
func (l *Logger) Subscribe(mask EventType) *Subscription { func (l *Logger) Subscribe(mask EventType) *Subscription {
l.mutex.Lock() l.mutex.Lock()
dl.Debugln("subscribe", mask) dl.Debugln("subscribe", mask)
s := &Subscription{ s := &Subscription{
mask: mask, mask: mask,
events: make(chan Event, BufferSize), events: make(chan Event, BufferSize),
timeout: time.NewTimer(0), timeout: time.NewTimer(0),
} }
// We need to create the timeout timer in the stopped, non-fired state so
// that Subscription.Poll() can safely reset it and select on the timeout
// channel. This ensures the timer is stopped and the channel drained.
if !s.timeout.Stop() {
<-s.timeout.C
}
l.subs = append(l.subs, s) l.subs = append(l.subs, s)
l.mutex.Unlock() l.mutex.Unlock()
return s return s
@@ -196,19 +205,18 @@ func (l *Logger) Unsubscribe(s *Subscription) {
func (s *Subscription) Poll(timeout time.Duration) (Event, error) { func (s *Subscription) Poll(timeout time.Duration) (Event, error) {
dl.Debugln("poll", timeout) dl.Debugln("poll", timeout)
if !s.timeout.Reset(timeout) { s.timeout.Reset(timeout)
select {
case <-s.timeout.C:
default:
}
}
select { select {
case e, ok := <-s.events: case e, ok := <-s.events:
if !ok { if !ok {
return e, ErrClosed return e, ErrClosed
} }
s.timeout.Stop() if !s.timeout.Stop() {
// The timeout must be stopped and possibly drained to be ready
// for reuse in the next call.
<-s.timeout.C
}
return e, nil return e, nil
case <-s.timeout.C: case <-s.timeout.C:
return Event{}, ErrTimeout return Event{}, ErrTimeout