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  }