go.temporal.io/server@v1.23.0/common/clock/event_time_source.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package clock 26 27 import ( 28 "sync" 29 "time" 30 ) 31 32 type ( 33 // EventTimeSource is a fake TimeSource. Unlike other fake clock implementations, the methods are synchronous, so 34 // when you call Advance or Update, all triggered timers from AfterFunc will fire before the method returns, in the 35 // same goroutine. 36 EventTimeSource struct { 37 mu sync.RWMutex 38 now time.Time 39 timers []*fakeTimer 40 } 41 42 // fakeTimer is a fake implementation of [Timer]. 43 fakeTimer struct { 44 // need a link to the parent timeSource for synchronization 45 timeSource *EventTimeSource 46 // deadline for when the timer should fire 47 deadline time.Time 48 // callback to call when the timer fires 49 callback func() 50 // done is true if the timer has fired or been stopped 51 done bool 52 // index of the timer in the parent timeSource 53 index int 54 } 55 ) 56 57 // NewEventTimeSource returns a EventTimeSource with the current time set to Unix zero: 1970-01-01 00:00:00 +0000 UTC. 58 func NewEventTimeSource() *EventTimeSource { 59 return &EventTimeSource{ 60 now: time.Unix(0, 0), 61 } 62 } 63 64 // Now return the current time. 65 func (ts *EventTimeSource) Now() time.Time { 66 ts.mu.RLock() 67 defer ts.mu.RUnlock() 68 69 return ts.now 70 } 71 72 // AfterFunc return a timer that will fire after the specified duration. It is important to note that the timeSource is 73 // locked while the callback is called. This means that you must be cautious about calling any other mutating methods on 74 // the timeSource from within the callback. Doing so will probably result in a deadlock. To avoid this, you may want to 75 // wrap all such calls in a goroutine. If the duration is non-positive, the callback will fire immediately before 76 // AfterFunc returns. 77 func (ts *EventTimeSource) AfterFunc(d time.Duration, f func()) Timer { 78 ts.mu.Lock() 79 defer ts.mu.Unlock() 80 81 if d < 0 { 82 d = 0 83 } 84 t := &fakeTimer{timeSource: ts, deadline: ts.now.Add(d), callback: f} 85 t.index = len(ts.timers) 86 ts.timers = append(ts.timers, t) 87 ts.fireTimers() 88 89 return t 90 } 91 92 // Update the fake current time. It returns the timeSource so that you can chain calls like this: 93 // timeSource := NewEventTimeSource().Update(time.Now()) 94 func (ts *EventTimeSource) Update(now time.Time) *EventTimeSource { 95 ts.mu.Lock() 96 defer ts.mu.Unlock() 97 98 ts.now = now 99 ts.fireTimers() 100 return ts 101 } 102 103 // Advance the timer by the specified duration. 104 func (ts *EventTimeSource) Advance(d time.Duration) { 105 ts.mu.Lock() 106 defer ts.mu.Unlock() 107 108 ts.now = ts.now.Add(d) 109 ts.fireTimers() 110 } 111 112 // fireTimers fires all timers that are ready. 113 func (ts *EventTimeSource) fireTimers() { 114 n := 0 115 for _, t := range ts.timers { 116 if t.deadline.After(ts.now) { 117 ts.timers[n] = t 118 t.index = n 119 n++ 120 } else { 121 t.callback() 122 t.done = true 123 } 124 } 125 ts.timers = ts.timers[:n] 126 } 127 128 // Reset the timer to fire after the specified duration. Returns true if the timer was active. 129 func (t *fakeTimer) Reset(d time.Duration) bool { 130 t.timeSource.mu.Lock() 131 defer t.timeSource.mu.Unlock() 132 133 if t.done { 134 return false 135 } 136 137 t.deadline = t.timeSource.now.Add(d) 138 t.timeSource.fireTimers() 139 return true 140 } 141 142 // Stop the timer. Returns true if the timer was active. 143 func (t *fakeTimer) Stop() bool { 144 t.timeSource.mu.Lock() 145 defer t.timeSource.mu.Unlock() 146 147 if t.done { 148 return false 149 } 150 151 i := t.index 152 timers := t.timeSource.timers 153 154 timers[i] = timers[len(timers)-1] // swap with last timer 155 timers[i].index = i // update index of swapped timer 156 timers = timers[:len(timers)-1] // shrink list 157 158 t.timeSource.timers = timers 159 t.done = true // ensure that the timer is not reused 160 161 return true 162 }