package scheduling import ( "sync" "time" ) type clock interface { now() float64 runAt(ts float64, f func()) close() } type scheduleInfo struct { ts float64 f func() } type systemClock struct { since time.Time schedule chan scheduleInfo wg sync.WaitGroup } func newSystemClock() *systemClock { c := &systemClock{ since: time.Now(), schedule: make(chan scheduleInfo), } c.start() return c } func (c *systemClock) now() float64 { return time.Since(c.since).Seconds() } func (c *systemClock) runAt(ts float64, f func()) { c.schedule <- scheduleInfo{ts: ts, f: f} } func (c *systemClock) close() { close(c.schedule) c.wg.Wait() } func (c *systemClock) start() { c.wg.Add(1) go func() { defer c.wg.Done() t := time.NewTimer(time.Hour) var f func() for { select { case <-t.C: if f != nil { f() f = nil } t.Reset(time.Hour) case s, ok := <-c.schedule: if !ok { return } now := c.now() if now >= s.ts { s.f() f = nil continue } if !t.Stop() { select { case <-t.C: default: } } t.Reset(time.Duration((s.ts - now) * 1e9)) f = s.f } } }() }