github.com/XiaoMi/Gaea@v1.2.5/util/timer/timer.go (about) 1 /* 2 Copyright 2017 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package timer provides various enhanced timer functions. 18 package timer 19 20 import ( 21 "sync" 22 "time" 23 24 "github.com/XiaoMi/Gaea/util/sync2" 25 ) 26 27 // Out-of-band messages 28 type typeAction int 29 30 const ( 31 timerStop typeAction = iota 32 timerReset 33 timerTrigger 34 ) 35 36 /* 37 Timer provides timer functionality that can be controlled 38 by the user. You start the timer by providing it a callback function, 39 which it will call at the specified interval. 40 41 var t = timer.NewTimer(1e9) 42 t.Start(KeepHouse) 43 44 func KeepHouse() { 45 // do house keeping work 46 } 47 48 You can stop the timer by calling t.Stop, which is guaranteed to 49 wait if KeepHouse is being executed. 50 51 You can create an untimely trigger by calling t.Trigger. You can also 52 schedule an untimely trigger by calling t.TriggerAfter. 53 54 The timer interval can be changed on the fly by calling t.SetInterval. 55 A zero value interval will cause the timer to wait indefinitely, and it 56 will react only to an explicit Trigger or Stop. 57 */ 58 type Timer struct { 59 interval sync2.AtomicDuration 60 61 // state management 62 mu sync.Mutex 63 running bool 64 65 // msg is used for out-of-band messages 66 msg chan typeAction 67 } 68 69 // NewTimer creates a new Timer object 70 func NewTimer(interval time.Duration) *Timer { 71 tm := &Timer{ 72 msg: make(chan typeAction), 73 } 74 tm.interval.Set(interval) 75 return tm 76 } 77 78 // Start starts the timer. 79 func (tm *Timer) Start(keephouse func()) { 80 tm.mu.Lock() 81 defer tm.mu.Unlock() 82 if tm.running { 83 return 84 } 85 tm.running = true 86 go tm.run(keephouse) 87 } 88 89 func (tm *Timer) run(keephouse func()) { 90 for { 91 var ch <-chan time.Time 92 interval := tm.interval.Get() 93 if interval <= 0 { 94 ch = nil 95 } else { 96 ch = time.After(interval) 97 } 98 select { 99 case action := <-tm.msg: 100 switch action { 101 case timerStop: 102 return 103 case timerReset: 104 continue 105 } 106 case <-ch: 107 } 108 keephouse() 109 } 110 } 111 112 // SetInterval changes the wait interval. 113 // It will cause the timer to restart the wait. 114 func (tm *Timer) SetInterval(ns time.Duration) { 115 tm.interval.Set(ns) 116 tm.mu.Lock() 117 defer tm.mu.Unlock() 118 if tm.running { 119 tm.msg <- timerReset 120 } 121 } 122 123 // Trigger will cause the timer to immediately execute the keephouse function. 124 // It will then cause the timer to restart the wait. 125 func (tm *Timer) Trigger() { 126 tm.mu.Lock() 127 defer tm.mu.Unlock() 128 if tm.running { 129 tm.msg <- timerTrigger 130 } 131 } 132 133 // TriggerAfter waits for the specified duration and triggers the next event. 134 func (tm *Timer) TriggerAfter(duration time.Duration) { 135 go func() { 136 time.Sleep(duration) 137 tm.Trigger() 138 }() 139 } 140 141 // Stop will stop the timer. It guarantees that the timer will not execute 142 // any more calls to keephouse once it has returned. 143 func (tm *Timer) Stop() { 144 tm.mu.Lock() 145 defer tm.mu.Unlock() 146 if tm.running { 147 tm.msg <- timerStop 148 tm.running = false 149 } 150 } 151 152 // Interval returns the current interval. 153 func (tm *Timer) Interval() time.Duration { 154 return tm.interval.Get() 155 }