go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/clock/systemtimer.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package clock 16 17 import ( 18 "context" 19 "time" 20 ) 21 22 type systemTimer struct { 23 // ctx is the underlying timer. It starts as nil, and is initialized on Reset. 24 ctx context.Context 25 26 // timerC is the timer channel. 27 timerC chan TimerResult 28 29 // timerStoppedC is a signal channel used by Stop to alert our monitor that 30 // this timer has been manually canceled. 31 timerStoppedC chan struct{} 32 // timerMonitorResultC returns true if the timer monitor was prematurely 33 // terminated, false if not. It is used by our timer monitor to indicate its 34 // status when stopped. 35 timerMonitorResultC chan bool 36 } 37 38 var _ Timer = (*systemTimer)(nil) 39 40 func newSystemTimer(ctx context.Context) Timer { 41 return &systemTimer{ 42 ctx: ctx, 43 timerC: make(chan TimerResult, 1), 44 } 45 } 46 47 func (t *systemTimer) GetC() <-chan TimerResult { 48 return t.timerC 49 } 50 51 func (t *systemTimer) Reset(d time.Duration) (running bool) { 52 running = t.Stop() 53 54 // If our Context is already done, finish immediately. 55 if err := t.ctx.Err(); err != nil { 56 t.timerC <- TimerResult{Time: time.Now(), Err: err} 57 return 58 } 59 60 // Start a monitor goroutine and our actual timer. Copy our channels, since 61 // future stop/reset will change the systemTimer's values and our goroutine 62 // should only operate on this round's values. 63 t.timerStoppedC = make(chan struct{}) 64 t.timerMonitorResultC = make(chan bool, 1) 65 66 timerStoppedC, timerMonitorResultC := t.timerStoppedC, t.timerMonitorResultC 67 68 realTimer := time.NewTimer(d) 69 go func() { 70 defer realTimer.Stop() 71 72 interrupted := false 73 defer func() { 74 timerMonitorResultC <- interrupted 75 close(timerMonitorResultC) 76 }() 77 78 select { 79 case <-timerStoppedC: 80 interrupted = true 81 82 case <-t.ctx.Done(): 83 t.timerC <- TimerResult{Time: time.Now(), Err: t.ctx.Err()} 84 85 case now := <-realTimer.C: 86 t.timerC <- TimerResult{Time: now, Err: nil} 87 } 88 }() 89 90 return 91 } 92 93 func (t *systemTimer) Stop() bool { 94 if t.timerStoppedC == nil { 95 return false 96 } 97 close(t.timerStoppedC) 98 t.timerStoppedC = nil 99 return <-t.timerMonitorResultC 100 }