You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
naza/pkg/mock/time.go

220 lines
4.1 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright 2021, Chef. All rights reserved.
// https://github.com/q191201771/naza
//
// Use of this source code is governed by a MIT-style license
// that can be found in the License file.
//
// Author: Chef (191201771@qq.com)
package mock
import (
"sort"
"sync"
"time"
)
// TODO(chef): [feat] 增加Clock::NewTicker
type Clock interface {
// Now NewTimer ...
//
// 标准库中的操作集合, stdClock和mockClock都有对应的实现
//
Now() time.Time
NewTimer(d time.Duration) *Timer
Sleep(d time.Duration)
// Add Set ...
//
// mockClock使用以下这些函数来修改当前时间
// 注意如果是stdClock则没有必要调用以下函数调用以下函数为空实现
//
Add(d time.Duration)
Set(t time.Time)
}
func NewStdClock() Clock {
return &stdClock{}
}
func NewFakeClock() Clock {
return &fakeClock{}
}
// ---------------------------------------------------------------------------------------------------------------------
type stdClock struct {
}
func (c *stdClock) Now() time.Time {
return time.Now()
}
func (c *stdClock) NewTimer(d time.Duration) *Timer {
stdTimer := time.NewTimer(d)
return &Timer{
C: stdTimer.C,
stdTimer: stdTimer,
}
}
func (c *stdClock) Sleep(d time.Duration) {
time.Sleep(d)
}
func (c *stdClock) Add(d time.Duration) {
// noop
}
func (c *stdClock) Set(t time.Time) {
// noop
}
// ---------------------------------------------------------------------------------------------------------------------
type fakeClock struct {
mu sync.Mutex
now time.Time
timers timers
}
func (fc *fakeClock) Now() time.Time {
fc.mu.Lock()
defer fc.mu.Unlock()
return fc.now
}
func (fc *fakeClock) NewTimer(d time.Duration) *Timer {
fc.mu.Lock()
defer fc.mu.Unlock()
ch := make(chan time.Time, 1)
t := &Timer{
C: ch,
c: ch,
fc: fc,
timing: fc.now.Add(d),
}
fc.addTimer(t)
return t
}
func (fc *fakeClock) Sleep(d time.Duration) {
// TODO(chef): [feat] 实现和add、set挂钩的Sleep内部用Timer实现等待
// 当前的使用场景都是测试场景直接快速跳过Sleep以及能够满足需求
}
func (fc *fakeClock) Add(d time.Duration) {
fc.mu.Lock()
fc.now = fc.now.Add(d)
fc.ringTimersIfNeeded()
fc.mu.Unlock()
time.Sleep(1 * time.Millisecond)
}
func (fc *fakeClock) Set(t time.Time) {
fc.mu.Lock()
fc.now = t
fc.ringTimersIfNeeded()
fc.mu.Unlock()
time.Sleep(1 * time.Millisecond)
}
func (fc *fakeClock) resetTimerWithLock(t *Timer, d time.Duration) bool {
fc.mu.Lock()
defer fc.mu.Unlock()
if t.expired || t.stopped {
return false
}
fc.delTimer(t)
t.timing = fc.now.Add(d)
fc.addTimer(t)
return true
}
func (fc *fakeClock) stopTimerWithLock(t *Timer) bool {
fc.mu.Lock()
defer fc.mu.Unlock()
if t.expired || t.stopped {
return false
}
fc.delTimer(t)
t.stopped = true
return true
}
func (fc *fakeClock) addTimer(t *Timer) {
fc.timers = append(fc.timers, t)
sort.Sort(fc.timers)
}
func (fc *fakeClock) delTimer(t *Timer) {
i := 0
for ; i < len(fc.timers); i++ {
if fc.timers[i] == t {
fc.timers = append(fc.timers[:i], fc.timers[i+1:]...)
}
}
}
func (fc *fakeClock) ringTimersIfNeeded() {
i := 0
for ; i < len(fc.timers); i++ {
if fc.timers[i].timing.After(fc.now) {
break
}
fc.timers[i].c <- fc.now
fc.timers[i].expired = true
}
fc.timers = fc.timers[i:]
}
// TODO(chef): [perf] 用有序map
type timers []*Timer
func (t timers) Len() int {
return len(t)
}
func (t timers) Less(i, j int) bool {
return t[i].timing.Before(t[j].timing)
}
func (t timers) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
// ---------------------------------------------------------------------------------------------------------------------
type Timer struct {
C <-chan time.Time
stdTimer *time.Timer
fc *fakeClock
timing time.Time
c chan time.Time
expired bool
stopped bool
}
func (t *Timer) Reset(d time.Duration) bool {
if t.stdTimer != nil {
return t.stdTimer.Reset(d)
}
return t.fc.resetTimerWithLock(t, d)
}
func (t *Timer) Stop() bool {
if t.stdTimer != nil {
return t.stdTimer.Stop()
}
return t.fc.stopTimerWithLock(t)
}