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  }