github.com/tdcblockchain/tdcblockchain@v0.0.0-20191111034745-805c65ade158/common/mclock/simclock.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package mclock 18 19 import ( 20 "sync" 21 "time" 22 ) 23 24 // Simulated implements a virtual Clock for reproducible time-sensitive tests. It 25 // simulates a scheduler on a virtual timescale where actual processing takes zero time. 26 // 27 // The virtual clock doesn't advance on its own, call Run to advance it and execute timers. 28 // Since there is no way to influence the Go scheduler, testing timeout behaviour involving 29 // goroutines needs special care. A good way to test such timeouts is as follows: First 30 // perform the action that is supposed to time out. Ensure that the timer you want to test 31 // is created. Then run the clock until after the timeout. Finally observe the effect of 32 // the timeout using a channel or semaphore. 33 type Simulated struct { 34 now AbsTime 35 scheduled []event 36 mu sync.RWMutex 37 cond *sync.Cond 38 lastId uint64 39 } 40 41 type event struct { 42 do func() 43 at AbsTime 44 id uint64 45 } 46 47 // SimulatedEvent implements Event for a virtual clock. 48 type SimulatedEvent struct { 49 at AbsTime 50 id uint64 51 s *Simulated 52 } 53 54 // Run moves the clock by the given duration, executing all timers before that duration. 55 func (s *Simulated) Run(d time.Duration) { 56 s.mu.Lock() 57 s.init() 58 59 end := s.now + AbsTime(d) 60 var do []func() 61 for len(s.scheduled) > 0 { 62 ev := s.scheduled[0] 63 if ev.at > end { 64 break 65 } 66 s.now = ev.at 67 do = append(do, ev.do) 68 s.scheduled = s.scheduled[1:] 69 } 70 s.now = end 71 s.mu.Unlock() 72 73 for _, fn := range do { 74 fn() 75 } 76 } 77 78 func (s *Simulated) ActiveTimers() int { 79 s.mu.RLock() 80 defer s.mu.RUnlock() 81 82 return len(s.scheduled) 83 } 84 85 func (s *Simulated) WaitForTimers(n int) { 86 s.mu.Lock() 87 defer s.mu.Unlock() 88 s.init() 89 90 for len(s.scheduled) < n { 91 s.cond.Wait() 92 } 93 } 94 95 // Now implements Clock. 96 func (s *Simulated) Now() AbsTime { 97 s.mu.RLock() 98 defer s.mu.RUnlock() 99 100 return s.now 101 } 102 103 // Sleep implements Clock. 104 func (s *Simulated) Sleep(d time.Duration) { 105 <-s.After(d) 106 } 107 108 // After implements Clock. 109 func (s *Simulated) After(d time.Duration) <-chan time.Time { 110 after := make(chan time.Time, 1) 111 s.AfterFunc(d, func() { 112 after <- (time.Time{}).Add(time.Duration(s.now)) 113 }) 114 return after 115 } 116 117 // AfterFunc implements Clock. 118 func (s *Simulated) AfterFunc(d time.Duration, do func()) Event { 119 s.mu.Lock() 120 defer s.mu.Unlock() 121 s.init() 122 123 at := s.now + AbsTime(d) 124 s.lastId++ 125 id := s.lastId 126 l, h := 0, len(s.scheduled) 127 ll := h 128 for l != h { 129 m := (l + h) / 2 130 if (at < s.scheduled[m].at) || ((at == s.scheduled[m].at) && (id < s.scheduled[m].id)) { 131 h = m 132 } else { 133 l = m + 1 134 } 135 } 136 s.scheduled = append(s.scheduled, event{}) 137 copy(s.scheduled[l+1:], s.scheduled[l:ll]) 138 e := event{do: do, at: at, id: id} 139 s.scheduled[l] = e 140 s.cond.Broadcast() 141 return &SimulatedEvent{at: at, id: id, s: s} 142 } 143 144 func (s *Simulated) init() { 145 if s.cond == nil { 146 s.cond = sync.NewCond(&s.mu) 147 } 148 } 149 150 // Cancel implements Event. 151 func (e *SimulatedEvent) Cancel() bool { 152 s := e.s 153 s.mu.Lock() 154 defer s.mu.Unlock() 155 156 l, h := 0, len(s.scheduled) 157 ll := h 158 for l != h { 159 m := (l + h) / 2 160 if e.id == s.scheduled[m].id { 161 l = m 162 break 163 } 164 if (e.at < s.scheduled[m].at) || ((e.at == s.scheduled[m].at) && (e.id < s.scheduled[m].id)) { 165 h = m 166 } else { 167 l = m + 1 168 } 169 } 170 if l >= ll || s.scheduled[l].id != e.id { 171 return false 172 } 173 copy(s.scheduled[l:ll-1], s.scheduled[l+1:]) 174 s.scheduled = s.scheduled[:ll-1] 175 return true 176 }