mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-31 01:59:39 -05:00
232 lines
4.3 KiB
Go
232 lines
4.3 KiB
Go
package timer
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
mutex sync.Mutex
|
|
timers []*Timer
|
|
rescheduleC = make(chan struct{}, 1)
|
|
)
|
|
|
|
func init() {
|
|
go timerRoutine()
|
|
}
|
|
|
|
// when is a helper function for setting the 'when' field of a runtimeTimer.
|
|
// It returns what the time will be, in nanoseconds, Duration d in the future.
|
|
// If d is negative, it is ignored. If the returned value would be less than
|
|
// zero because of an overflow, MaxInt64 is returned.
|
|
func when(d time.Duration) int64 {
|
|
if d <= 0 {
|
|
return time.Now().UnixNano()
|
|
}
|
|
t := time.Now().UnixNano() + int64(d)
|
|
if t < 0 {
|
|
t = 1<<63 - 1 // math.MaxInt64
|
|
}
|
|
return t
|
|
}
|
|
|
|
// Add the timer to the heap.
|
|
func addTimer(t *Timer, d time.Duration) {
|
|
t.when = when(d)
|
|
|
|
mutex.Lock()
|
|
addTimerLocked(t)
|
|
mutex.Unlock()
|
|
}
|
|
|
|
func addTimerLocked(t *Timer) {
|
|
t.i = len(timers)
|
|
timers = append(timers, t)
|
|
siftupTimer(t.i)
|
|
|
|
// Reschedule if this is the next timer in the heap.
|
|
if t.i == 0 {
|
|
reschedule()
|
|
}
|
|
}
|
|
|
|
// Delete timer t from the heap.
|
|
// It returns true if t was removed, false if t wasn't even there.
|
|
// Do not need to update the timer routine: if it wakes up early, no big deal.
|
|
func delTimer(t *Timer) (b bool) {
|
|
mutex.Lock()
|
|
b = delTimerLocked(t)
|
|
mutex.Unlock()
|
|
return
|
|
}
|
|
|
|
// Delete timer t from the heap.
|
|
// It returns true if t was removed, false if t wasn't even there.
|
|
// Do not need to update the timer routine: if it wakes up early, no big deal.
|
|
func delTimerLocked(t *Timer) bool {
|
|
// t may not be registered anymore and may have
|
|
// a bogus i (typically 0, if generated by Go).
|
|
// Verify it before proceeding.
|
|
i := t.i
|
|
last := len(timers) - 1
|
|
if i < 0 || i > last || timers[i] != t {
|
|
return false
|
|
}
|
|
if i != last {
|
|
timers[i] = timers[last]
|
|
timers[i].i = i
|
|
}
|
|
timers[last] = nil
|
|
timers = timers[:last]
|
|
if i != last {
|
|
siftupTimer(i)
|
|
siftdownTimer(i)
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Reset the timer to the new timeout duration.
|
|
// This clears the channel.
|
|
func resetTimer(t *Timer, d time.Duration) (b bool) {
|
|
mutex.Lock()
|
|
b = delTimerLocked(t)
|
|
t.reset()
|
|
t.when = when(d)
|
|
addTimerLocked(t)
|
|
mutex.Unlock()
|
|
return
|
|
}
|
|
|
|
func reschedule() {
|
|
// Do not block if there is already a pending reschedule request.
|
|
select {
|
|
case rescheduleC <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|
|
|
|
func timerRoutine() {
|
|
var now time.Time
|
|
var delta int64
|
|
var last int
|
|
|
|
var sleepTimerActive bool
|
|
sleepTimer := time.NewTimer(time.Second)
|
|
sleepTimer.Stop()
|
|
|
|
Loop:
|
|
for {
|
|
select {
|
|
case <-sleepTimer.C:
|
|
|
|
case <-rescheduleC:
|
|
// If not yet received a value from sleepTimer.C, the timer must be
|
|
// stopped and—if Stop reports that the timer expired before being
|
|
// stopped—the channel explicitly drained.
|
|
if !sleepTimer.Stop() && sleepTimerActive {
|
|
<-sleepTimer.C
|
|
}
|
|
}
|
|
sleepTimerActive = false
|
|
|
|
Reschedule:
|
|
now = time.Now()
|
|
|
|
mutex.Lock()
|
|
if len(timers) == 0 {
|
|
mutex.Unlock()
|
|
continue Loop
|
|
}
|
|
|
|
t := timers[0]
|
|
delta = t.when - now.UnixNano()
|
|
|
|
// Sleep if not expired.
|
|
if delta > 0 {
|
|
mutex.Unlock()
|
|
sleepTimer.Reset(time.Duration(delta))
|
|
sleepTimerActive = true
|
|
continue Loop
|
|
}
|
|
|
|
// Timer expired. Trigger the timer's function callback.
|
|
t.f(&now)
|
|
|
|
// Remove from heap.
|
|
last = len(timers) - 1
|
|
if last > 0 {
|
|
timers[0] = timers[last]
|
|
timers[0].i = 0
|
|
}
|
|
timers[last] = nil
|
|
timers = timers[:last]
|
|
if last > 0 {
|
|
siftdownTimer(0)
|
|
}
|
|
t.i = -1 // mark as removed
|
|
|
|
mutex.Unlock()
|
|
|
|
// Reschedule immediately.
|
|
goto Reschedule
|
|
}
|
|
}
|
|
|
|
// Heap maintenance algorithms.
|
|
// Based on golang source /runtime/time.go
|
|
|
|
func siftupTimer(i int) {
|
|
tmp := timers[i]
|
|
when := tmp.when
|
|
|
|
var p int
|
|
for i > 0 {
|
|
p = (i - 1) / 4 // parent
|
|
if when >= timers[p].when {
|
|
break
|
|
}
|
|
timers[i] = timers[p]
|
|
timers[i].i = i
|
|
timers[p] = tmp
|
|
timers[p].i = p
|
|
i = p
|
|
}
|
|
}
|
|
|
|
func siftdownTimer(i int) {
|
|
n := len(timers)
|
|
when := timers[i].when
|
|
tmp := timers[i]
|
|
for {
|
|
c := i*4 + 1 // left child
|
|
c3 := c + 2 // mid child
|
|
if c >= n {
|
|
break
|
|
}
|
|
w := timers[c].when
|
|
if c+1 < n && timers[c+1].when < w {
|
|
w = timers[c+1].when
|
|
c++
|
|
}
|
|
if c3 < n {
|
|
w3 := timers[c3].when
|
|
if c3+1 < n && timers[c3+1].when < w3 {
|
|
w3 = timers[c3+1].when
|
|
c3++
|
|
}
|
|
if w3 < w {
|
|
w = w3
|
|
c = c3
|
|
}
|
|
}
|
|
if w >= when {
|
|
break
|
|
}
|
|
timers[i] = timers[c]
|
|
timers[i].i = i
|
|
timers[c] = tmp
|
|
timers[c].i = c
|
|
i = c
|
|
}
|
|
}
|