github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/timeutil/manual_time.go (about)

     1  // Copyright 2020 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package timeutil
    12  
    13  import (
    14  	"container/heap"
    15  	"container/list"
    16  	"fmt"
    17  	"sort"
    18  	"time"
    19  
    20  	"github.com/cockroachdb/cockroachdb-parser/pkg/util/syncutil"
    21  )
    22  
    23  // ManualTime is a testing implementation of TimeSource.
    24  type ManualTime struct {
    25  	mu struct {
    26  		syncutil.Mutex
    27  		now    time.Time
    28  		timers manualTimerQueue
    29  		// tickers is a list with element type *manualTicker.
    30  		tickers list.List
    31  	}
    32  }
    33  
    34  // NewManualTime constructs a new ManualTime.
    35  func NewManualTime(initialTime time.Time) *ManualTime {
    36  	mt := ManualTime{}
    37  	mt.mu.now = initialTime
    38  	mt.mu.timers = manualTimerQueue{
    39  		m: make(map[*manualTimer]int),
    40  	}
    41  	mt.mu.tickers.Init()
    42  	return &mt
    43  }
    44  
    45  var _ TimeSource = (*ManualTime)(nil)
    46  
    47  // Now returns the current time.
    48  func (m *ManualTime) Now() time.Time {
    49  	m.mu.Lock()
    50  	defer m.mu.Unlock()
    51  	return m.mu.now
    52  }
    53  
    54  // Since implements TimeSource interface
    55  func (m *ManualTime) Since(t time.Time) time.Duration {
    56  	return m.Now().Sub(t)
    57  }
    58  
    59  // NewTimer constructs a new timer.
    60  func (m *ManualTime) NewTimer() TimerI {
    61  	return &manualTimer{m: m}
    62  }
    63  
    64  // NewTicker creates a new ticker.
    65  func (m *ManualTime) NewTicker(duration time.Duration) TickerI {
    66  	if duration <= 0 {
    67  		panic("non-positive interval for NewTicker")
    68  	}
    69  	m.mu.Lock()
    70  	defer m.mu.Unlock()
    71  
    72  	t := &manualTicker{
    73  		m:        m,
    74  		duration: duration,
    75  		nextTick: m.mu.now.Add(duration),
    76  		// We allocate a big buffer so that sending a tick never blocks.
    77  		ch: make(chan time.Time, 10000),
    78  	}
    79  	t.element = m.mu.tickers.PushBack(t)
    80  	return t
    81  }
    82  
    83  // Advance forwards the current time by the given duration.
    84  func (m *ManualTime) Advance(duration time.Duration) {
    85  	m.AdvanceTo(m.Now().Add(duration))
    86  }
    87  
    88  // Backwards moves the clock back by duration. Duration is expected to be
    89  // positive, and it will be subtracted from the current time.
    90  func (m *ManualTime) Backwards(duration time.Duration) {
    91  	if duration < 0 {
    92  		panic("invalid negative duration")
    93  	}
    94  	m.mu.Lock()
    95  	defer m.mu.Unlock()
    96  	// No timers fire when the clock goes backwards.
    97  	m.mu.now = m.mu.now.Add(-duration)
    98  }
    99  
   100  // AdvanceTo advances the current time to t. If t is earlier than the current
   101  // time then AdvanceTo is a no-op.
   102  func (m *ManualTime) AdvanceTo(now time.Time) {
   103  	m.mu.Lock()
   104  	defer m.mu.Unlock()
   105  	m.advanceToLocked(now)
   106  }
   107  
   108  // MustAdvanceTo is like AdvanceTo, except it panics if now is below m's current time.
   109  func (m *ManualTime) MustAdvanceTo(now time.Time) {
   110  	m.mu.Lock()
   111  	defer m.mu.Unlock()
   112  	if now.Before(m.mu.now) {
   113  		panic(fmt.Sprintf("attempting to move ManualTime backwards from %s to %s", m.mu.now, now))
   114  	}
   115  	m.advanceToLocked(now)
   116  }
   117  
   118  func (m *ManualTime) advanceToLocked(now time.Time) {
   119  	if !now.After(m.mu.now) {
   120  		return
   121  	}
   122  	m.mu.now = now
   123  
   124  	// Fire off any timers.
   125  	for m.mu.timers.Len() > 0 {
   126  		next := m.mu.timers.heap[0]
   127  		if next.at.After(now) {
   128  			break
   129  		}
   130  		next.ch <- next.at
   131  		heap.Pop(&m.mu.timers)
   132  	}
   133  
   134  	// Fire off any tickers.
   135  	for e := m.mu.tickers.Front(); e != nil; e = e.Next() {
   136  		t := e.Value.(*manualTicker)
   137  		for !t.nextTick.After(now) {
   138  			select {
   139  			case t.ch <- t.nextTick:
   140  			default:
   141  				panic("ticker channel full")
   142  			}
   143  			t.nextTick = t.nextTick.Add(t.duration)
   144  		}
   145  	}
   146  }
   147  
   148  func (m *ManualTime) add(mt *manualTimer) {
   149  	m.mu.Lock()
   150  	defer m.mu.Unlock()
   151  
   152  	if !mt.at.After(m.mu.now) {
   153  		mt.ch <- mt.at
   154  	} else {
   155  		heap.Push(&m.mu.timers, mt)
   156  	}
   157  }
   158  
   159  func (m *ManualTime) removeTimer(mt *manualTimer) bool {
   160  	m.mu.Lock()
   161  	defer m.mu.Unlock()
   162  	if idx, ok := m.mu.timers.m[mt]; ok {
   163  		heap.Remove(&m.mu.timers, idx)
   164  		return true
   165  	}
   166  	return false
   167  }
   168  
   169  func (m *ManualTime) removeTicker(t *manualTicker) {
   170  	m.mu.Lock()
   171  	defer m.mu.Unlock()
   172  	if t.element != nil {
   173  		m.mu.tickers.Remove(t.element)
   174  		t.element = nil
   175  	}
   176  }
   177  
   178  // Timers returns a snapshot of the timestamps of the pending timers.
   179  func (m *ManualTime) Timers() []time.Time {
   180  	m.mu.Lock()
   181  	defer m.mu.Unlock()
   182  	timers := make([]time.Time, m.mu.timers.Len())
   183  	for i, t := range m.mu.timers.heap {
   184  		timers[i] = t.at
   185  	}
   186  	sort.Slice(timers, func(i, j int) bool {
   187  		return timers[i].Before(timers[j])
   188  	})
   189  	return timers
   190  }
   191  
   192  type manualTimerQueue struct {
   193  	// m maintains the index for a timer in heap.
   194  	m    map[*manualTimer]int
   195  	heap []*manualTimer
   196  }
   197  
   198  var _ heap.Interface = (*manualTimerQueue)(nil)
   199  
   200  func (m *manualTimerQueue) Len() int {
   201  	return len(m.heap)
   202  }
   203  
   204  func (m *manualTimerQueue) Less(i, j int) bool {
   205  	return m.heap[i].at.Before(m.heap[j].at)
   206  }
   207  
   208  func (m *manualTimerQueue) Swap(i, j int) {
   209  	m.heap[i], m.heap[j] = m.heap[j], m.heap[i]
   210  	m.m[m.heap[i]] = i
   211  	m.m[m.heap[j]] = j
   212  }
   213  
   214  func (m *manualTimerQueue) Push(x interface{}) {
   215  	mt := x.(*manualTimer)
   216  	m.m[mt] = len(m.heap)
   217  	m.heap = append(m.heap, mt)
   218  }
   219  
   220  func (m *manualTimerQueue) Pop() interface{} {
   221  	lastIdx := len(m.heap) - 1
   222  	ret := m.heap[lastIdx]
   223  	delete(m.m, ret)
   224  	m.heap = m.heap[:lastIdx]
   225  	return ret
   226  }
   227  
   228  type manualTimer struct {
   229  	m  *ManualTime
   230  	at time.Time
   231  	ch chan time.Time
   232  }
   233  
   234  var _ TimerI = (*manualTimer)(nil)
   235  
   236  func (m *manualTimer) Reset(duration time.Duration) {
   237  	m.Stop()
   238  	m.at = m.m.Now().Add(duration)
   239  	m.ch = make(chan time.Time, 1)
   240  	m.m.add(m)
   241  }
   242  
   243  func (m *manualTimer) Stop() bool {
   244  	removed := m.m.removeTimer(m)
   245  	m.ch = nil
   246  	m.at = time.Time{}
   247  	return removed
   248  }
   249  
   250  func (m *manualTimer) Ch() <-chan time.Time {
   251  	return m.ch
   252  }
   253  
   254  func (m *manualTimer) MarkRead() {}
   255  
   256  type manualTicker struct {
   257  	m       *ManualTime
   258  	element *list.Element
   259  
   260  	duration time.Duration
   261  	nextTick time.Time
   262  	ch       chan time.Time
   263  }
   264  
   265  // Reset is part of the TickerI interface.
   266  func (t *manualTicker) Reset(duration time.Duration) {
   267  	panic("not implemented")
   268  }
   269  
   270  // Stop is part of the TickerI interface.
   271  func (t *manualTicker) Stop() {
   272  	t.m.removeTicker(t)
   273  }
   274  
   275  // Ch is part of the TickerI interface.
   276  func (t *manualTicker) Ch() <-chan time.Time {
   277  	return t.ch
   278  }