github.com/jimmyx0x/go-ethereum@v1.10.28/common/mclock/alarm.go (about) 1 // Copyright 2022 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package mclock 18 19 import ( 20 "time" 21 ) 22 23 // Alarm sends timed notifications on a channel. This is very similar to a regular timer, 24 // but is easier to use in code that needs to re-schedule the same timer over and over. 25 // 26 // When scheduling an Alarm, the channel returned by C() will receive a value no later 27 // than the scheduled time. An Alarm can be reused after it has fired and can also be 28 // canceled by calling Stop. 29 type Alarm struct { 30 ch chan struct{} 31 clock Clock 32 timer Timer 33 deadline AbsTime 34 } 35 36 // NewAlarm creates an Alarm. 37 func NewAlarm(clock Clock) *Alarm { 38 if clock == nil { 39 panic("nil clock") 40 } 41 return &Alarm{ 42 ch: make(chan struct{}, 1), 43 clock: clock, 44 } 45 } 46 47 // C returns the alarm notification channel. This channel remains identical for 48 // the entire lifetime of the alarm, and is never closed. 49 func (e *Alarm) C() <-chan struct{} { 50 return e.ch 51 } 52 53 // Stop cancels the alarm and drains the channel. 54 // This method is not safe for concurrent use. 55 func (e *Alarm) Stop() { 56 // Clear timer. 57 if e.timer != nil { 58 e.timer.Stop() 59 } 60 e.deadline = 0 61 62 // Drain the channel. 63 select { 64 case <-e.ch: 65 default: 66 } 67 } 68 69 // Schedule sets the alarm to fire no later than the given time. If the alarm was already 70 // scheduled but has not fired yet, it may fire earlier than the newly-scheduled time. 71 func (e *Alarm) Schedule(time AbsTime) { 72 now := e.clock.Now() 73 e.schedule(now, time) 74 } 75 76 func (e *Alarm) schedule(now, newDeadline AbsTime) { 77 if e.timer != nil { 78 if e.deadline > now && e.deadline <= newDeadline { 79 // Here, the current timer can be reused because it is already scheduled to 80 // occur earlier than the new deadline. 81 // 82 // The e.deadline > now part of the condition is important. If the old 83 // deadline lies in the past, we assume the timer has already fired and needs 84 // to be rescheduled. 85 return 86 } 87 e.timer.Stop() 88 } 89 90 // Set the timer. 91 d := time.Duration(0) 92 if newDeadline < now { 93 newDeadline = now 94 } else { 95 d = newDeadline.Sub(now) 96 } 97 e.timer = e.clock.AfterFunc(d, e.send) 98 e.deadline = newDeadline 99 } 100 101 func (e *Alarm) send() { 102 select { 103 case e.ch <- struct{}{}: 104 default: 105 } 106 }