go.temporal.io/server@v1.23.0/common/timer/remote_gate.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 timer 26 27 import ( 28 "sync" 29 "time" 30 ) 31 32 type ( 33 RemoteGate interface { 34 Gate 35 // SetCurrentTime set the current time, and additionally fire the fire chan 36 // if new "current" time is after the next wakeup time, return true if 37 // "current" is actually updated 38 SetCurrentTime(nextTime time.Time) bool 39 } 40 41 RemoteGateImpl struct { 42 // the channel which will be used to proxy the fired timer 43 fireCh chan struct{} 44 45 // lock for timer and next wakeup time 46 sync.Mutex 47 // view of current time 48 currentTime time.Time 49 // time which timer will fire 50 nextWakeupTime time.Time 51 } 52 ) 53 54 // NewRemoteGate create a new timer gate instance 55 func NewRemoteGate() RemoteGate { 56 rg := &RemoteGateImpl{ 57 currentTime: time.Time{}, 58 nextWakeupTime: time.Time{}, 59 fireCh: make(chan struct{}, 1), 60 } 61 return rg 62 } 63 64 // FireCh return the channel which will be fired when time is up 65 func (rg *RemoteGateImpl) FireCh() <-chan struct{} { 66 return rg.fireCh 67 } 68 69 // FireAfter check will the timer get fired after a certain time 70 func (rg *RemoteGateImpl) FireAfter(now time.Time) bool { 71 rg.Lock() 72 defer rg.Unlock() 73 74 active := rg.currentTime.Before(rg.nextWakeupTime) 75 return active && rg.nextWakeupTime.After(now) 76 } 77 78 // Update the timer gate, return true if update is a success. 79 // Success means timer is idle or timer is set with a sooner time to fire 80 func (rg *RemoteGateImpl) Update(nextTime time.Time) bool { 81 rg.Lock() 82 defer rg.Unlock() 83 84 active := rg.currentTime.Before(rg.nextWakeupTime) 85 if active { 86 if rg.nextWakeupTime.Before(nextTime) { 87 // current time < next wake up time < next time 88 return false 89 } 90 91 if rg.currentTime.Before(nextTime) { 92 // current time < next time <= next wake-up time 93 rg.nextWakeupTime = nextTime 94 return true 95 } 96 97 // next time <= current time < next wake-up time 98 rg.nextWakeupTime = nextTime 99 rg.fire() 100 return true 101 } 102 103 // this means the timer, before stopped, has already fired / never active 104 if !rg.currentTime.Before(nextTime) { 105 // next time is <= current time, need to fire immediately 106 // whether to update next wake-up time or not is irrelevant 107 rg.fire() 108 } else { 109 // next time > current time 110 rg.nextWakeupTime = nextTime 111 } 112 return true 113 } 114 115 // Close shutdown the timer 116 func (rg *RemoteGateImpl) Close() { 117 // no op 118 } 119 120 // SetCurrentTime set the current time, and additionally fire the fire chan 121 // if new "current" time is after the next wake-up time, return true if 122 // "current" is actually updated 123 func (rg *RemoteGateImpl) SetCurrentTime(currentTime time.Time) bool { 124 rg.Lock() 125 defer rg.Unlock() 126 127 if !rg.currentTime.Before(currentTime) { 128 // new current time is <= current time 129 return false 130 } 131 132 // NOTE: do not update the current time now 133 if !rg.currentTime.Before(rg.nextWakeupTime) { 134 // current time already >= next wakeup time 135 // avoid duplicate fire 136 rg.currentTime = currentTime 137 return true 138 } 139 140 rg.currentTime = currentTime 141 if !rg.currentTime.Before(rg.nextWakeupTime) { 142 rg.fire() 143 } 144 return true 145 } 146 147 func (rg *RemoteGateImpl) fire() { 148 select { 149 case rg.fireCh <- struct{}{}: 150 // timer successfully triggered 151 default: 152 // timer already triggered, pass 153 } 154 }