mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-15 13:03:00 +08:00
217 lines
4.3 KiB
Go
217 lines
4.3 KiB
Go
package manualclock
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
runtimeclock "UAmask/internal/clock"
|
|
)
|
|
|
|
type Clock struct {
|
|
advanceMu sync.Mutex
|
|
mu sync.Mutex
|
|
now time.Time
|
|
nextID uint64
|
|
timers map[*scheduled]struct{}
|
|
}
|
|
|
|
type scheduled struct {
|
|
owner *Clock
|
|
id uint64
|
|
due time.Time
|
|
interval time.Duration
|
|
channel chan time.Time
|
|
callback func()
|
|
active bool
|
|
ticker bool
|
|
}
|
|
|
|
type Timer struct {
|
|
scheduled *scheduled
|
|
}
|
|
|
|
type Ticker struct {
|
|
scheduled *scheduled
|
|
}
|
|
|
|
var _ runtimeclock.Clock = (*Clock)(nil)
|
|
var _ runtimeclock.Timer = (*Timer)(nil)
|
|
var _ runtimeclock.Ticker = (*Ticker)(nil)
|
|
|
|
func New(start time.Time) *Clock {
|
|
return &Clock{
|
|
now: start,
|
|
timers: make(map[*scheduled]struct{}),
|
|
}
|
|
}
|
|
|
|
func (clock *Clock) Now() time.Time {
|
|
clock.mu.Lock()
|
|
defer clock.mu.Unlock()
|
|
return clock.now
|
|
}
|
|
|
|
func (clock *Clock) After(duration time.Duration) <-chan time.Time {
|
|
return clock.NewTimer(duration).C()
|
|
}
|
|
|
|
func (clock *Clock) AfterFunc(duration time.Duration, callback func()) runtimeclock.Timer {
|
|
return &Timer{scheduled: clock.schedule(duration, 0, nil, callback, false)}
|
|
}
|
|
|
|
func (clock *Clock) NewTimer(duration time.Duration) runtimeclock.Timer {
|
|
return &Timer{scheduled: clock.schedule(duration, 0, make(chan time.Time, 1), nil, false)}
|
|
}
|
|
|
|
func (clock *Clock) NewTicker(duration time.Duration) runtimeclock.Ticker {
|
|
if duration <= 0 {
|
|
panic("non-positive interval for NewTicker")
|
|
}
|
|
return &Ticker{scheduled: clock.schedule(duration, duration, make(chan time.Time, 1), nil, true)}
|
|
}
|
|
|
|
func (clock *Clock) Advance(duration time.Duration) {
|
|
if duration < 0 {
|
|
panic("manual clock cannot advance backwards")
|
|
}
|
|
|
|
clock.advanceMu.Lock()
|
|
defer clock.advanceMu.Unlock()
|
|
|
|
clock.mu.Lock()
|
|
target := clock.now.Add(duration)
|
|
clock.mu.Unlock()
|
|
|
|
for {
|
|
clock.mu.Lock()
|
|
next := clock.nextDueLocked(target)
|
|
if next == nil {
|
|
clock.now = target
|
|
clock.mu.Unlock()
|
|
return
|
|
}
|
|
|
|
firedAt := next.due
|
|
clock.now = firedAt
|
|
if next.ticker {
|
|
next.due = next.due.Add(next.interval)
|
|
next.id = clock.allocateIDLocked()
|
|
} else {
|
|
next.active = false
|
|
}
|
|
channel := next.channel
|
|
callback := next.callback
|
|
clock.mu.Unlock()
|
|
|
|
if channel != nil {
|
|
select {
|
|
case channel <- firedAt:
|
|
default:
|
|
}
|
|
}
|
|
if callback != nil {
|
|
callback()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (clock *Clock) Pending() int {
|
|
clock.mu.Lock()
|
|
defer clock.mu.Unlock()
|
|
count := 0
|
|
for timer := range clock.timers {
|
|
if timer.active {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
func (clock *Clock) Due() int {
|
|
clock.mu.Lock()
|
|
defer clock.mu.Unlock()
|
|
count := 0
|
|
for timer := range clock.timers {
|
|
if timer.active && !timer.due.After(clock.now) {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
func (clock *Clock) schedule(duration, interval time.Duration, channel chan time.Time, callback func(), ticker bool) *scheduled {
|
|
clock.mu.Lock()
|
|
defer clock.mu.Unlock()
|
|
if duration < 0 {
|
|
duration = 0
|
|
}
|
|
timer := &scheduled{
|
|
owner: clock,
|
|
id: clock.allocateIDLocked(),
|
|
due: clock.now.Add(duration),
|
|
interval: interval,
|
|
channel: channel,
|
|
callback: callback,
|
|
active: true,
|
|
ticker: ticker,
|
|
}
|
|
clock.timers[timer] = struct{}{}
|
|
return timer
|
|
}
|
|
|
|
func (clock *Clock) allocateIDLocked() uint64 {
|
|
clock.nextID++
|
|
return clock.nextID
|
|
}
|
|
|
|
func (clock *Clock) nextDueLocked(target time.Time) *scheduled {
|
|
var next *scheduled
|
|
for timer := range clock.timers {
|
|
if !timer.active || timer.due.After(target) {
|
|
continue
|
|
}
|
|
if next == nil || timer.due.Before(next.due) || (timer.due.Equal(next.due) && timer.id < next.id) {
|
|
next = timer
|
|
}
|
|
}
|
|
return next
|
|
}
|
|
|
|
func (timer *Timer) C() <-chan time.Time {
|
|
return timer.scheduled.channel
|
|
}
|
|
|
|
func (timer *Timer) Stop() bool {
|
|
return timer.scheduled.stop()
|
|
}
|
|
|
|
func (timer *Timer) Reset(duration time.Duration) bool {
|
|
scheduled := timer.scheduled
|
|
scheduled.owner.mu.Lock()
|
|
defer scheduled.owner.mu.Unlock()
|
|
if duration < 0 {
|
|
duration = 0
|
|
}
|
|
wasActive := scheduled.active
|
|
scheduled.active = true
|
|
scheduled.due = scheduled.owner.now.Add(duration)
|
|
scheduled.id = scheduled.owner.allocateIDLocked()
|
|
return wasActive
|
|
}
|
|
|
|
func (ticker *Ticker) C() <-chan time.Time {
|
|
return ticker.scheduled.channel
|
|
}
|
|
|
|
func (ticker *Ticker) Stop() {
|
|
_ = ticker.scheduled.stop()
|
|
}
|
|
|
|
func (timer *scheduled) stop() bool {
|
|
timer.owner.mu.Lock()
|
|
defer timer.owner.mu.Unlock()
|
|
wasActive := timer.active
|
|
timer.active = false
|
|
return wasActive
|
|
}
|