├── LICENSE ├── README.md ├── go.mod ├── timer.go ├── timer_test.go └── timers.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2016 Roland Singer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Timer implementation with a fixed Reset behavior 2 | 3 | [![GoDoc](https://godoc.org/github.com/desertbit/timer?status.svg)](https://godoc.org/github.com/desertbit/timer) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/desertbit/timer)](https://goreportcard.com/report/github.com/desertbit/timer) 5 | 6 | This is a lightweight timer implementation which is a drop-in replacement for 7 | Go's Timer. Reset behaves as one would expect and drains the timer.C channel automatically. 8 | The core design of this package is similar to the original runtime timer implementation. 9 | 10 | These two lines are equivalent except for saving some garbage: 11 | 12 | ```go 13 | t.Reset(x) 14 | 15 | t := timer.NewTimer(x) 16 | ``` 17 | 18 | See issues: 19 | - https://github.com/golang/go/issues/11513 20 | - https://github.com/golang/go/issues/14383 21 | - https://github.com/golang/go/issues/12721 22 | - https://github.com/golang/go/issues/14038 23 | - https://groups.google.com/forum/#!msg/golang-dev/c9UUfASVPoU/tlbK2BpFEwAJ 24 | - http://grokbase.com/t/gg/golang-nuts/1571eh3tv7/go-nuts-reusing-time-timer 25 | 26 | Quote from the [Timer Go doc reference](https://golang.org/pkg/time/#Timer): 27 | 28 | >Reset changes the timer to expire after duration d. 29 | It returns true if the timer had been active, false if the timer had 30 | expired or been stopped. 31 | 32 | > To reuse an active timer, always call its Stop method first and—if it had 33 | expired—drain the value from its channel. For example: [...] 34 | This should not be done concurrent to other receives from the Timer's channel. 35 | 36 | > Note that it is not possible to use Reset's return value correctly, as there 37 | is a race condition between draining the channel and the new timer expiring. 38 | Reset should always be used in concert with Stop, as described above. 39 | The return value exists to preserve compatibility with existing programs. 40 | 41 | ## Broken behavior sample 42 | 43 | ### Sample 1 44 | 45 | ```go 46 | package main 47 | 48 | import ( 49 | "log" 50 | "time" 51 | ) 52 | 53 | func main() { 54 | start := time.Now() 55 | 56 | // Start a new timer with a timeout of 1 second. 57 | timer := time.NewTimer(1 * time.Second) 58 | 59 | // Wait for 2 seconds. 60 | // Meanwhile the timer fired and filled the channel. 61 | time.Sleep(2 * time.Second) 62 | 63 | // Reset the timer. This should act exactly as creating a new timer. 64 | timer.Reset(1 * time.Second) 65 | 66 | // However this will fire immediately, because the channel was not drained. 67 | // See issue: https://github.com/golang/go/issues/11513 68 | <-timer.C 69 | 70 | if int(time.Since(start).Seconds()) != 3 { 71 | log.Fatalf("took ~%v seconds, should be ~3 seconds\n", int(time.Since(start).Seconds())) 72 | } 73 | } 74 | ``` 75 | 76 | ### Sample 2 77 | 78 | ```go 79 | package main 80 | 81 | import "time" 82 | 83 | const ( 84 | keepaliveInterval = 2 * time.Millisecond 85 | ) 86 | 87 | var ( 88 | resetC = make(chan struct{}, 1) 89 | ) 90 | 91 | func main() { 92 | go keepaliveLoop() 93 | 94 | // Sample routine triggering the reset. 95 | // Example: this could be due to incoming peer requests and 96 | // a keepalive check should be reset to the max keepalive timeout. 97 | for i := 0; i < 1000; i++ { 98 | time.Sleep(time.Millisecond) 99 | resetKeepalive() 100 | } 101 | } 102 | 103 | func resetKeepalive() { 104 | // Don't block if there is already a reset request. 105 | select { 106 | case resetC <- struct{}{}: 107 | default: 108 | } 109 | } 110 | 111 | func keepaliveLoop() { 112 | t := time.NewTimer(keepaliveInterval) 113 | 114 | for { 115 | select { 116 | case <-resetC: 117 | time.Sleep(3 * time.Millisecond) // Simulate some reset work... 118 | t.Reset(keepaliveInterval) 119 | case <-t.C: 120 | ping() 121 | t.Reset(keepaliveInterval) 122 | } 123 | } 124 | } 125 | 126 | func ping() { 127 | panic("ping must not be called in this example") 128 | } 129 | ``` 130 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/desertbit/timer 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /timer.go: -------------------------------------------------------------------------------- 1 | // Package timer is a Go timer implementation with a fixed Reset behavior. 2 | package timer 3 | 4 | import ( 5 | "time" 6 | ) 7 | 8 | // The Timer type represents a single event. When the Timer expires, 9 | // the current time will be sent on C, unless the Timer was created by AfterFunc. 10 | // A Timer must be created with NewTimer. NewStoppedTimer or AfterFunc. 11 | type Timer struct { 12 | C <-chan time.Time 13 | 14 | i int // heap index. 15 | when time.Time // Timer wakes up at when. 16 | 17 | // f is called in a locked context on timeout. This function must not block 18 | // and must behave well-defined. 19 | f func(t *time.Time) 20 | 21 | // reset is called in a locked context. This function must not block 22 | // and must behave well-defined. 23 | reset func() 24 | } 25 | 26 | // NewTimer creates a new Timer that will send the current time on its 27 | // channel after at least duration d. 28 | func NewTimer(d time.Duration) *Timer { 29 | t := NewStoppedTimer() 30 | addTimer(t, d) 31 | return t 32 | } 33 | 34 | // NewStoppedTimer creates a new stopped Timer. 35 | func NewStoppedTimer() *Timer { 36 | c := make(chan time.Time, 1) 37 | t := &Timer{ 38 | C: c, 39 | f: func(t *time.Time) { 40 | // Don't block. 41 | select { 42 | case c <- *t: 43 | default: 44 | } 45 | }, 46 | reset: func() { 47 | // Empty the channel if filled. 48 | select { 49 | case <-c: 50 | default: 51 | } 52 | }, 53 | } 54 | return t 55 | } 56 | 57 | // Stop prevents the Timer from firing. 58 | // It returns true if the call stops the timer, 59 | // false if the timer has already expired or been stopped. 60 | // Stop does not close the channel, to prevent a read from 61 | // the channel succeeding incorrectly. 62 | func (t *Timer) Stop() (wasActive bool) { 63 | if t.f == nil { 64 | panic("timer: Stop called on uninitialized Timer") 65 | } 66 | return delTimer(t) 67 | } 68 | 69 | // Reset changes the timer to expire after duration d. 70 | // It returns true if the timer had been active, 71 | // false if the timer had expired or been stopped. 72 | // The channel t.C is cleared and calling t.Reset() behaves as creating a 73 | // new Timer. 74 | func (t *Timer) Reset(d time.Duration) bool { 75 | if t.f == nil { 76 | panic("timer: Reset called on uninitialized Timer") 77 | } 78 | return resetTimer(t, d) 79 | } 80 | -------------------------------------------------------------------------------- /timer_test.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestNullTimout(t *testing.T) { 10 | // Timeout for 0 seconds. 11 | start := time.Now() 12 | timer := NewTimer(0) 13 | <-timer.C 14 | if int(time.Since(start).Seconds()) != 0 { 15 | t.Errorf("took ~%v seconds, should be ~0 seconds\n", int(time.Since(start).Seconds())) 16 | } 17 | } 18 | 19 | func TestNegativeTimout(t *testing.T) { 20 | // Timeout for -1 seconds. 21 | start := time.Now() 22 | timer := NewTimer(-1) 23 | <-timer.C 24 | if int(time.Since(start).Seconds()) != 0 { 25 | t.Errorf("took ~%v seconds, should be ~0 seconds\n", int(time.Since(start).Seconds())) 26 | } 27 | 28 | // Timeout for -100 seconds. 29 | start = time.Now() 30 | timer = NewTimer(-100 * time.Second) 31 | <-timer.C 32 | if int(time.Since(start).Seconds()) != 0 { 33 | t.Errorf("took ~%v seconds, should be ~0 seconds\n", int(time.Since(start).Seconds())) 34 | } 35 | } 36 | 37 | func TestTimeValue(t *testing.T) { 38 | // Timeout for 0 seconds. 39 | start := time.Now() 40 | timer := NewTimer(time.Second) 41 | v := <-timer.C 42 | if diff := v.Sub(start).Seconds(); int(diff) != 1 { 43 | t.Errorf("invalid time value: %v", int(diff)) 44 | } 45 | } 46 | 47 | func TestSingleTimout(t *testing.T) { 48 | // Timeout for 1 second and wait. 49 | start := time.Now() 50 | timer := NewTimer(time.Second) 51 | <-timer.C 52 | if int(time.Since(start).Seconds()) != 1 { 53 | t.Errorf("took ~%v seconds, should be ~1 seconds\n", int(time.Since(start).Seconds())) 54 | } 55 | } 56 | 57 | func TestMultipleTimouts(t *testing.T) { 58 | start := time.Now() 59 | var timers []*Timer 60 | 61 | for i := 0; i < 1000; i++ { 62 | timers = append(timers, NewTimer(time.Second)) 63 | } 64 | 65 | // Wait for them all to expire. 66 | for _, timer := range timers { 67 | <-timer.C 68 | } 69 | 70 | if int(time.Since(start).Seconds()) != 1 { 71 | t.Errorf("took ~%v seconds, should be ~1 seconds\n", int(time.Since(start).Seconds())) 72 | } 73 | } 74 | 75 | func TestMultipleDifferentTimouts(t *testing.T) { 76 | start := time.Now() 77 | var timers []*Timer 78 | 79 | for i := 0; i < 1000; i++ { 80 | timers = append(timers, NewTimer(time.Duration(i%4)*time.Second)) 81 | } 82 | 83 | // Wait for them all to expire. 84 | for _, timer := range timers { 85 | <-timer.C 86 | } 87 | 88 | if int(time.Since(start).Seconds()) != 3 { 89 | t.Errorf("took ~%v seconds, should be ~3 seconds\n", int(time.Since(start).Seconds())) 90 | } 91 | } 92 | 93 | func TestStoppedTimer(t *testing.T) { 94 | timer := NewStoppedTimer() 95 | if !timer.when.IsZero() { 96 | t.Errorf("invalid stopped timer when value") 97 | } 98 | 99 | start := time.Now() 100 | wasActive := timer.Reset(time.Second) 101 | if wasActive { 102 | t.Errorf("stopped timer: was active is true") 103 | } 104 | 105 | <-timer.C 106 | if int(time.Since(start).Seconds()) != 1 { 107 | t.Errorf("took ~%v seconds, should be ~1 seconds\n", int(time.Since(start).Seconds())) 108 | } 109 | } 110 | 111 | func TestStop(t *testing.T) { 112 | timer := NewTimer(time.Second) 113 | wasActive := timer.Stop() 114 | if !wasActive { 115 | t.Errorf("stop timer: was active is false") 116 | } 117 | 118 | select { 119 | case <-timer.C: 120 | t.Errorf("failed to stop timer") 121 | case <-time.After(2 * time.Second): 122 | } 123 | 124 | wasActive = timer.Stop() 125 | if wasActive { 126 | t.Errorf("stop timer: was active is true") 127 | } 128 | } 129 | 130 | func TestStopPanic(t *testing.T) { 131 | defer func() { 132 | r := recover() 133 | if r == nil || r.(string) != "timer: Stop called on uninitialized Timer" { 134 | t.Errorf("stop timer: invalid stop panic") 135 | } 136 | }() 137 | 138 | timer := &Timer{} 139 | timer.Stop() 140 | } 141 | 142 | func TestMultipleStop(t *testing.T) { 143 | var timers []*Timer 144 | 145 | for i := 0; i < 1000; i++ { 146 | timer := NewTimer(time.Second) 147 | wasActive := timer.Stop() 148 | if !wasActive { 149 | t.Errorf("stop timer: was active is false") 150 | } 151 | 152 | timers = append(timers, timer) 153 | } 154 | 155 | time.Sleep(2 * time.Second) 156 | 157 | // All channels must block. 158 | for _, timer := range timers { 159 | select { 160 | case <-timer.C: 161 | t.Errorf("failed to stop timer") 162 | default: 163 | } 164 | } 165 | 166 | for _, timer := range timers { 167 | wasActive := timer.Stop() 168 | if wasActive { 169 | t.Errorf("stop timer: was active is true") 170 | } 171 | } 172 | } 173 | 174 | func TestReset(t *testing.T) { 175 | start := time.Now() 176 | timer := NewTimer(time.Second) 177 | wasActive := timer.Reset(2 * time.Second) 178 | if !wasActive { 179 | t.Errorf("reset timer: was active is false") 180 | } 181 | 182 | <-timer.C 183 | 184 | if int(time.Since(start).Seconds()) != 2 { 185 | t.Errorf("took ~%v seconds, should be ~2 seconds\n", int(time.Since(start).Seconds())) 186 | } 187 | 188 | start = time.Now() 189 | wasActive = timer.Reset(time.Second) 190 | if wasActive { 191 | t.Errorf("reset timer: was active is true") 192 | } 193 | 194 | <-timer.C 195 | 196 | if int(time.Since(start).Seconds()) != 1 { 197 | t.Errorf("took ~%v seconds, should be ~1 seconds\n", int(time.Since(start).Seconds())) 198 | } 199 | } 200 | 201 | func TestNegativeReset(t *testing.T) { 202 | // Timeout for -1 seconds. 203 | start := time.Now() 204 | timer := NewTimer(time.Second) 205 | timer.Reset(-1) 206 | <-timer.C 207 | if int(time.Since(start).Seconds()) != 0 { 208 | t.Errorf("took ~%v seconds, should be ~0 seconds\n", int(time.Since(start).Seconds())) 209 | } 210 | 211 | // Timeout for -100 seconds. 212 | start = time.Now() 213 | timer = NewTimer(time.Second) 214 | timer.Reset(-100 * time.Second) 215 | <-timer.C 216 | if int(time.Since(start).Seconds()) != 0 { 217 | t.Errorf("took ~%v seconds, should be ~0 seconds\n", int(time.Since(start).Seconds())) 218 | } 219 | } 220 | 221 | func TestMultipleResets(t *testing.T) { 222 | start := time.Now() 223 | var timers []*Timer 224 | 225 | for i := 0; i < 1000; i++ { 226 | timer := NewTimer(time.Second) 227 | timers = append(timers, timer) 228 | timer.Reset(2 * time.Second) 229 | } 230 | 231 | // Wait for them all to expire. 232 | for _, timer := range timers { 233 | <-timer.C 234 | } 235 | 236 | if int(time.Since(start).Seconds()) != 2 { 237 | t.Errorf("took ~%v seconds, should be ~2 seconds\n", int(time.Since(start).Seconds())) 238 | } 239 | } 240 | 241 | func TestMultipleZeroResets(t *testing.T) { 242 | start := time.Now() 243 | var timers []*Timer 244 | 245 | for i := 0; i < 1000; i++ { 246 | timer := NewTimer(time.Second) 247 | timers = append(timers, timer) 248 | timer.Reset(0) 249 | } 250 | 251 | // Wait for them all to expire. 252 | for _, timer := range timers { 253 | <-timer.C 254 | } 255 | 256 | if int(time.Since(start).Seconds()) != 0 { 257 | t.Errorf("took ~%v seconds, should be ~0 seconds\n", int(time.Since(start).Seconds())) 258 | } 259 | } 260 | 261 | func TestResetChannelClear(t *testing.T) { 262 | timer := NewTimer(0) 263 | time.Sleep(time.Second) 264 | 265 | if len(timer.C) != 1 { 266 | t.Errorf("reset timer: channel should be filled") 267 | } 268 | 269 | wasActive := timer.Reset(2 * time.Second) 270 | if wasActive { 271 | t.Errorf("reset timer: was active is true") 272 | } 273 | 274 | if len(timer.C) != 0 { 275 | t.Errorf("reset timer: channel should be empty") 276 | } 277 | 278 | start := time.Now() 279 | <-timer.C 280 | 281 | if int(time.Since(start).Seconds()) != 2 { 282 | t.Errorf("took ~%v seconds, should be ~2 seconds\n", int(time.Since(start).Seconds())) 283 | } 284 | } 285 | 286 | func TestResetPanic(t *testing.T) { 287 | defer func() { 288 | r := recover() 289 | if r == nil || r.(string) != "timer: Reset called on uninitialized Timer" { 290 | t.Errorf("reset timer: invalid reset panic") 291 | } 292 | }() 293 | 294 | timer := &Timer{} 295 | timer.Reset(0) 296 | } 297 | 298 | func TestResetBehavior(t *testing.T) { 299 | start := time.Now() 300 | 301 | // Start a new timer with a timeout of 1 second. 302 | timer := NewTimer(1 * time.Second) 303 | 304 | // Wait for 2 seconds. 305 | // Meanwhile the timer fired filled the channel. 306 | time.Sleep(2 * time.Second) 307 | 308 | // Reset the timer. This should act exactly as creating a new timer. 309 | timer.Reset(1 * time.Second) 310 | 311 | // However this will fire immediately, because the channel was not drained. 312 | // See issue: https://github.com/golang/go/issues/11513 313 | <-timer.C 314 | 315 | if int(time.Since(start).Seconds()) != 3 { 316 | t.Errorf("took ~%v seconds, should be ~3 seconds\n", int(time.Since(start).Seconds())) 317 | } 318 | } 319 | 320 | func TestMultipleTimersForValidTimeouts(t *testing.T) { 321 | var wg sync.WaitGroup 322 | 323 | for i := 0; i < 1000; i++ { 324 | dur := time.Duration(i%11) * time.Second 325 | start := time.Now() 326 | timer := NewTimer(dur) 327 | wg.Add(1) 328 | go func() { 329 | dur /= time.Second 330 | <-timer.C 331 | if int(time.Since(start).Seconds()) != int(dur) { 332 | t.Errorf("took ~%v seconds, should be ~%v seconds\n", int(time.Since(start).Seconds()), int(dur)) 333 | } 334 | wg.Done() 335 | }() 336 | } 337 | 338 | wg.Wait() 339 | } 340 | 341 | func TestMultipleTimersConcurrentAddRemove(t *testing.T) { 342 | var wg sync.WaitGroup 343 | 344 | for i := 0; i < 100000; i++ { 345 | timer := NewTimer(time.Nanosecond) 346 | wg.Add(1) 347 | go func() { 348 | <-timer.C 349 | wg.Done() 350 | }() 351 | } 352 | 353 | wg.Wait() 354 | } 355 | -------------------------------------------------------------------------------- /timers.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | var ( 9 | mutex sync.Mutex 10 | timers []*Timer 11 | rescheduleC = make(chan struct{}, 1) 12 | ) 13 | 14 | func init() { 15 | go timerRoutine() 16 | } 17 | 18 | // Add the timer to the heap. 19 | func addTimer(t *Timer, d time.Duration) { 20 | t.when = time.Now().Add(d) 21 | 22 | mutex.Lock() 23 | addTimerLocked(t) 24 | mutex.Unlock() 25 | } 26 | 27 | func addTimerLocked(t *Timer) { 28 | t.i = len(timers) 29 | timers = append(timers, t) 30 | siftupTimer(t.i) 31 | 32 | // Reschedule if this is the next timer in the heap. 33 | if t.i == 0 { 34 | reschedule() 35 | } 36 | } 37 | 38 | // Delete timer t from the heap. 39 | // It returns true if t was removed, false if t wasn't even there. 40 | // Do not need to update the timer routine: if it wakes up early, no big deal. 41 | func delTimer(t *Timer) (b bool) { 42 | mutex.Lock() 43 | b = delTimerLocked(t) 44 | mutex.Unlock() 45 | return 46 | } 47 | 48 | // Delete timer t from the heap. 49 | // It returns true if t was removed, false if t wasn't even there. 50 | // Do not need to update the timer routine: if it wakes up early, no big deal. 51 | func delTimerLocked(t *Timer) bool { 52 | // t may not be registered anymore and may have 53 | // a bogus i (typically 0, if generated by Go). 54 | // Verify it before proceeding. 55 | i := t.i 56 | last := len(timers) - 1 57 | if i < 0 || i > last || timers[i] != t { 58 | return false 59 | } 60 | if i != last { 61 | timers[i] = timers[last] 62 | timers[i].i = i 63 | } 64 | timers[last] = nil 65 | timers = timers[:last] 66 | if i != last { 67 | siftupTimer(i) 68 | siftdownTimer(i) 69 | } 70 | return true 71 | } 72 | 73 | // Reset the timer to the new timeout duration. 74 | // This clears the channel. 75 | func resetTimer(t *Timer, d time.Duration) (b bool) { 76 | mutex.Lock() 77 | b = delTimerLocked(t) 78 | t.reset() 79 | t.when = time.Now().Add(d) 80 | addTimerLocked(t) 81 | mutex.Unlock() 82 | return 83 | } 84 | 85 | func reschedule() { 86 | // Do not block if there is already a pending reschedule request. 87 | select { 88 | case rescheduleC <- struct{}{}: 89 | default: 90 | } 91 | } 92 | 93 | func timerRoutine() { 94 | var now time.Time 95 | var last int 96 | 97 | var sleepTimerActive bool 98 | sleepTimer := time.NewTimer(time.Second) 99 | sleepTimer.Stop() 100 | 101 | Loop: 102 | for { 103 | select { 104 | case <-sleepTimer.C: 105 | 106 | case <-rescheduleC: 107 | // If not yet received a value from sleepTimer.C, the timer must be 108 | // stopped and—if Stop reports that the timer expired before being 109 | // stopped—the channel explicitly drained. 110 | if !sleepTimer.Stop() && sleepTimerActive { 111 | <-sleepTimer.C 112 | } 113 | } 114 | sleepTimerActive = false 115 | 116 | Reschedule: 117 | now = time.Now() 118 | 119 | mutex.Lock() 120 | if len(timers) == 0 { 121 | mutex.Unlock() 122 | continue Loop 123 | } 124 | 125 | t := timers[0] 126 | delta := t.when.Sub(now) 127 | 128 | // Sleep if not expired. 129 | if delta > 0 { 130 | mutex.Unlock() 131 | sleepTimer.Reset(delta) 132 | sleepTimerActive = true 133 | continue Loop 134 | } 135 | 136 | // Timer expired. Trigger the timer's function callback. 137 | t.f(&now) 138 | 139 | // Remove from heap. 140 | last = len(timers) - 1 141 | if last > 0 { 142 | timers[0] = timers[last] 143 | timers[0].i = 0 144 | } 145 | timers[last] = nil 146 | timers = timers[:last] 147 | if last > 0 { 148 | siftdownTimer(0) 149 | } 150 | t.i = -1 // mark as removed 151 | 152 | mutex.Unlock() 153 | 154 | // Reschedule immediately. 155 | goto Reschedule 156 | } 157 | } 158 | 159 | // Heap maintenance algorithms. 160 | // Based on golang source /runtime/time.go 161 | 162 | func siftupTimer(i int) { 163 | tmp := timers[i] 164 | when := tmp.when 165 | 166 | var p int 167 | for i > 0 { 168 | p = (i - 1) / 4 // parent 169 | if !when.Before(timers[p].when) { 170 | break 171 | } 172 | timers[i] = timers[p] 173 | timers[i].i = i 174 | timers[p] = tmp 175 | timers[p].i = p 176 | i = p 177 | } 178 | } 179 | 180 | func siftdownTimer(i int) { 181 | n := len(timers) 182 | when := timers[i].when 183 | tmp := timers[i] 184 | for { 185 | c := i*4 + 1 // left child 186 | c3 := c + 2 // mid child 187 | if c >= n { 188 | break 189 | } 190 | w := timers[c].when 191 | if c+1 < n && timers[c+1].when.Before(w) { 192 | w = timers[c+1].when 193 | c++ 194 | } 195 | if c3 < n { 196 | w3 := timers[c3].when 197 | if c3+1 < n && timers[c3+1].when.Before(w3) { 198 | w3 = timers[c3+1].when 199 | c3++ 200 | } 201 | if w3.Before(w) { 202 | w = w3 203 | c = c3 204 | } 205 | } 206 | if !w.Before(when) { 207 | break 208 | } 209 | timers[i] = timers[c] 210 | timers[i].i = i 211 | timers[c] = tmp 212 | timers[c].i = c 213 | i = c 214 | } 215 | } 216 | --------------------------------------------------------------------------------