go.temporal.io/server@v1.23.0/common/timer/local_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 "time" 29 30 "go.temporal.io/server/common/clock" 31 ) 32 33 type ( 34 LocalGate interface { 35 Gate 36 } 37 38 LocalGateImpl struct { 39 // the channel which will be used to proxy the fired timer 40 fireCh chan struct{} 41 closeCh chan struct{} 42 43 timeSource clock.TimeSource 44 45 // the actual timer which will fire 46 timer *time.Timer 47 // variable indicating when the above timer will fire 48 nextWakeupTime time.Time 49 } 50 ) 51 52 // NewLocalGate create a new timer gate instance 53 func NewLocalGate(timeSource clock.TimeSource) LocalGate { 54 lg := &LocalGateImpl{ 55 timer: time.NewTimer(0), 56 nextWakeupTime: time.Time{}, 57 fireCh: make(chan struct{}, 1), 58 closeCh: make(chan struct{}), 59 timeSource: timeSource, 60 } 61 // the timer should be stopped when initialized 62 if !lg.timer.Stop() { 63 // drain the existing signal if exist 64 <-lg.timer.C 65 } 66 67 go func() { 68 defer close(lg.fireCh) 69 defer lg.timer.Stop() 70 loop: 71 for { 72 select { 73 case <-lg.timer.C: 74 select { 75 // re-transmit on gateC 76 case lg.fireCh <- struct{}{}: 77 default: 78 } 79 80 case <-lg.closeCh: 81 // closed; cleanup and quit 82 break loop 83 } 84 } 85 }() 86 87 return lg 88 } 89 90 // FireCh return the channel which will be fired when time is up 91 func (lg *LocalGateImpl) FireCh() <-chan struct{} { 92 return lg.fireCh 93 } 94 95 // FireAfter check will the timer get fired after a certain time 96 func (lg *LocalGateImpl) FireAfter(now time.Time) bool { 97 return lg.nextWakeupTime.After(now) 98 } 99 100 // Update the timer gate, return true if update is a success. 101 // Success means timer is idle or timer is set with a sooner time to fire 102 func (lg *LocalGateImpl) Update(nextTime time.Time) bool { 103 // NOTE: negative duration will make the timer fire immediately 104 now := lg.timeSource.Now() 105 106 if lg.timer.Stop() && lg.nextWakeupTime.Before(nextTime) { 107 // this means the timer, before stopped, is active && next wake-up time do not have to be updated 108 lg.timer.Reset(lg.nextWakeupTime.Sub(now)) 109 return false 110 } 111 112 // this means the timer, before stopped, is active && next wake-up time has to be updated 113 // or this means the timer, before stopped, is already fired / never active 114 lg.nextWakeupTime = nextTime 115 lg.timer.Reset(nextTime.Sub(now)) 116 // Notifies caller that next notification is reset to fire at passed in 'next' visibility time 117 return true 118 } 119 120 // Close shutdown the timer 121 func (lg *LocalGateImpl) Close() { 122 close(lg.closeCh) 123 }