github.com/MetalBlockchain/metalgo@v1.11.9/utils/timer/timer.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package timer
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  )
    10  
    11  // Timer wraps a timer object. This allows a user to specify a handler. Once
    12  // specifying the handler, the dispatch thread can be called. The dispatcher
    13  // will only return after calling Stop. SetTimeoutIn will result in calling the
    14  // handler in the specified amount of time.
    15  type Timer struct {
    16  	handler func()
    17  	timeout chan struct{}
    18  
    19  	lock                    sync.Mutex
    20  	wg                      sync.WaitGroup
    21  	finished, shouldExecute bool
    22  	duration                time.Duration
    23  }
    24  
    25  // NewTimer creates a new timer object
    26  func NewTimer(handler func()) *Timer {
    27  	timer := &Timer{
    28  		handler: handler,
    29  		timeout: make(chan struct{}, 1),
    30  	}
    31  	timer.wg.Add(1)
    32  
    33  	return timer
    34  }
    35  
    36  // SetTimeoutIn will set the timer to fire the handler in [duration]
    37  func (t *Timer) SetTimeoutIn(duration time.Duration) {
    38  	t.lock.Lock()
    39  	defer t.lock.Unlock()
    40  
    41  	t.duration = duration
    42  	t.shouldExecute = true
    43  	t.reset()
    44  }
    45  
    46  // Cancel the currently scheduled event
    47  func (t *Timer) Cancel() {
    48  	t.lock.Lock()
    49  	defer t.lock.Unlock()
    50  
    51  	t.shouldExecute = false
    52  	t.reset()
    53  }
    54  
    55  // Stop this timer from executing any more.
    56  func (t *Timer) Stop() {
    57  	t.lock.Lock()
    58  	if !t.finished {
    59  		defer t.wg.Wait()
    60  	}
    61  	defer t.lock.Unlock()
    62  
    63  	t.finished = true
    64  	t.reset()
    65  }
    66  
    67  func (t *Timer) Dispatch() {
    68  	t.lock.Lock()
    69  	defer t.lock.Unlock()
    70  	defer t.wg.Done()
    71  
    72  	timer := time.NewTimer(0)
    73  	cleared := false
    74  	reset := false
    75  	for !t.finished { // t.finished needs to be thread safe
    76  		if !reset && !timer.Stop() && !cleared {
    77  			<-timer.C
    78  		}
    79  
    80  		if cleared && t.shouldExecute {
    81  			t.lock.Unlock()
    82  			t.handler()
    83  		} else {
    84  			t.lock.Unlock()
    85  		}
    86  
    87  		cleared = false
    88  		reset = false
    89  		select {
    90  		case <-t.timeout:
    91  			t.lock.Lock()
    92  			if t.shouldExecute {
    93  				timer.Reset(t.duration)
    94  			}
    95  			reset = true
    96  		case <-timer.C:
    97  			t.lock.Lock()
    98  			cleared = true
    99  		}
   100  	}
   101  }
   102  
   103  func (t *Timer) reset() {
   104  	select {
   105  	case t.timeout <- struct{}{}:
   106  	default:
   107  	}
   108  }