github.com/uber/kraken@v0.1.4/utils/timeutil/timer.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package timeutil 15 16 import ( 17 "sync" 18 "time" 19 ) 20 21 // Timer is a thread-safe adaptation of time.Timer intended for timeouts which 22 // may be periodically invalidated. A Timer can be started and cancelled multiple 23 // times before the Timer fires. Once a Timer fires, it cannot be used again. 24 type Timer struct { 25 // C will be closed once the Timer fires. 26 C chan struct{} 27 28 sync.Mutex 29 timer *time.Timer 30 cancel chan bool 31 duration time.Duration 32 } 33 34 // NewTimer creates a new Timer which is set to the given duration. 35 func NewTimer(d time.Duration) *Timer { 36 return &Timer{ 37 C: make(chan struct{}), 38 cancel: make(chan bool), 39 duration: d, 40 } 41 } 42 43 // Start starts the Timer. Returns false if the timer has already started, or 44 // if the timer has already fired. 45 func (t *Timer) Start() bool { 46 t.Lock() 47 defer t.Unlock() 48 49 if t.timer != nil { 50 // Timer has already started. 51 return false 52 } 53 t.timer = time.NewTimer(t.duration) 54 55 // Must copy this reference since t.timer will be nil if Cancel is called. 56 c := t.timer.C 57 58 go func() { 59 select { 60 case <-c: 61 close(t.C) 62 case <-t.cancel: 63 } 64 }() 65 66 return true 67 } 68 69 // Cancel cancels the Timer. Returns false if the timer has not started, or 70 // if the timer has already fired. 71 func (t *Timer) Cancel() bool { 72 t.Lock() 73 defer t.Unlock() 74 75 if t.timer == nil { 76 // Timer has not started. 77 return false 78 } 79 if !t.timer.Stop() { 80 // Timer already fired. 81 return false 82 } 83 // Let the goroutine created by Start exit. 84 t.cancel <- true 85 t.timer = nil 86 return true 87 }