github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/common/mclock/simclock.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:34</date> 10 //</624450074071535616> 11 12 13 package mclock 14 15 import ( 16 "sync" 17 "time" 18 ) 19 20 //模拟实现了一个可重复的时间敏感测试的虚拟时钟。它 21 //在实际处理占用零时间的虚拟时间刻度上模拟调度程序。 22 // 23 //虚拟时钟本身不前进,调用run前进并执行计时器。 24 //由于无法影响Go调度程序,因此测试涉及的超时行为 25 //戈罗蒂内斯需要特别照顾。测试这种超时的一个好方法是:首先 26 //执行应该超时的操作。确保要测试的计时器 27 //创建。然后运行时钟直到超时之后。最后观察 28 //使用通道或信号量的超时。 29 type Simulated struct { 30 now AbsTime 31 scheduled []event 32 mu sync.RWMutex 33 cond *sync.Cond 34 } 35 36 type event struct { 37 do func() 38 at AbsTime 39 } 40 41 //RUN按给定的持续时间移动时钟,在该持续时间之前执行所有计时器。 42 func (s *Simulated) Run(d time.Duration) { 43 s.mu.Lock() 44 defer s.mu.Unlock() 45 s.init() 46 47 end := s.now + AbsTime(d) 48 for len(s.scheduled) > 0 { 49 ev := s.scheduled[0] 50 if ev.at > end { 51 break 52 } 53 s.now = ev.at 54 ev.do() 55 s.scheduled = s.scheduled[1:] 56 } 57 s.now = end 58 } 59 60 func (s *Simulated) ActiveTimers() int { 61 s.mu.RLock() 62 defer s.mu.RUnlock() 63 64 return len(s.scheduled) 65 } 66 67 func (s *Simulated) WaitForTimers(n int) { 68 s.mu.Lock() 69 defer s.mu.Unlock() 70 s.init() 71 72 for len(s.scheduled) < n { 73 s.cond.Wait() 74 } 75 } 76 77 //现在实现时钟。 78 func (s *Simulated) Now() AbsTime { 79 s.mu.RLock() 80 defer s.mu.RUnlock() 81 82 return s.now 83 } 84 85 //睡眠实现时钟。 86 func (s *Simulated) Sleep(d time.Duration) { 87 <-s.After(d) 88 } 89 90 //在执行时钟之后。 91 func (s *Simulated) After(d time.Duration) <-chan time.Time { 92 after := make(chan time.Time, 1) 93 s.insert(d, func() { 94 after <- (time.Time{}).Add(time.Duration(s.now)) 95 }) 96 return after 97 } 98 99 func (s *Simulated) insert(d time.Duration, do func()) { 100 s.mu.Lock() 101 defer s.mu.Unlock() 102 s.init() 103 104 at := s.now + AbsTime(d) 105 l, h := 0, len(s.scheduled) 106 ll := h 107 for l != h { 108 m := (l + h) / 2 109 if at < s.scheduled[m].at { 110 h = m 111 } else { 112 l = m + 1 113 } 114 } 115 s.scheduled = append(s.scheduled, event{}) 116 copy(s.scheduled[l+1:], s.scheduled[l:ll]) 117 s.scheduled[l] = event{do: do, at: at} 118 s.cond.Broadcast() 119 } 120 121 func (s *Simulated) init() { 122 if s.cond == nil { 123 s.cond = sync.NewCond(&s.mu) 124 } 125 } 126