istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/loadbalancersim/timer/queue.go (about)

     1  //  Copyright Istio 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 timer
    16  
    17  import (
    18  	"container/heap"
    19  	"sync"
    20  	"time"
    21  )
    22  
    23  type Queue struct {
    24  	heap            timerHeap
    25  	mutex           sync.Mutex
    26  	stopCh          chan struct{}
    27  	resetTimerCh    chan struct{}
    28  	stopping        bool
    29  	timer           *time.Timer
    30  	currentDeadline time.Time
    31  }
    32  
    33  func NewQueue() *Queue {
    34  	q := &Queue{
    35  		heap:         make(timerHeap, 0),
    36  		timer:        time.NewTimer(1 * time.Minute),
    37  		stopCh:       make(chan struct{}),
    38  		resetTimerCh: make(chan struct{}),
    39  	}
    40  
    41  	// Start the worker thread.
    42  	go func() {
    43  		for {
    44  			select {
    45  			case <-q.stopCh:
    46  				q.stopTimer()
    47  				return
    48  			case <-q.resetTimerCh:
    49  				q.resetTimer()
    50  			case <-q.timer.C:
    51  				q.onTimerExpired()
    52  			}
    53  		}
    54  	}()
    55  
    56  	return q
    57  }
    58  
    59  func (q *Queue) Len() int {
    60  	q.mutex.Lock()
    61  	defer q.mutex.Unlock()
    62  	return q.heap.Len()
    63  }
    64  
    65  func (q *Queue) Schedule(handler func(), deadline time.Time) {
    66  	// Add the timer to the heap.
    67  	q.mutex.Lock()
    68  	heap.Push(&q.heap, &entry{
    69  		handler:  handler,
    70  		deadline: deadline,
    71  		index:    0,
    72  	})
    73  	q.mutex.Unlock()
    74  
    75  	// Request that the timer be reset.
    76  	q.resetTimerCh <- struct{}{}
    77  }
    78  
    79  func (q *Queue) ShutDown() {
    80  	close(q.stopCh)
    81  }
    82  
    83  func (q *Queue) stopTimer() {
    84  	q.mutex.Lock()
    85  	q.stopping = true
    86  	q.mutex.Unlock()
    87  
    88  	q.timer.Stop()
    89  }
    90  
    91  func (q *Queue) resetTimer() {
    92  	// Below is a separate function to limit the scope of the lock.
    93  	// We don't want to lock when we modify the timer in case it causes
    94  	// an immediate callback, which would reacquire the lock.
    95  	needReset, resetDuration := func() (bool, time.Duration) {
    96  		q.mutex.Lock()
    97  		defer q.mutex.Unlock()
    98  
    99  		if q.stopping {
   100  			// Ignore the event, since we're already shutting down.
   101  			return false, 0
   102  		}
   103  
   104  		e := q.heap.peek()
   105  		if e == nil || e.deadline.Equal(q.currentDeadline) {
   106  			// nothing to do.
   107  			return false, 0
   108  		}
   109  
   110  		q.currentDeadline = e.deadline
   111  		return true, time.Until(e.deadline)
   112  	}()
   113  
   114  	// Reset the timer.
   115  	if needReset {
   116  		q.timer.Reset(resetDuration)
   117  	}
   118  }
   119  
   120  func (q *Queue) onTimerExpired() {
   121  	// Collect all expired timers.
   122  	q.mutex.Lock()
   123  	handlers := q.heap.advanceTo(time.Now())
   124  	q.mutex.Unlock()
   125  
   126  	// Call the expired timer handlers.
   127  	for _, h := range handlers {
   128  		h()
   129  	}
   130  
   131  	// Reset the timer based on the earliest deadline.
   132  	q.resetTimer()
   133  }
   134  
   135  type entry struct {
   136  	deadline time.Time
   137  	handler  func()
   138  	index    int
   139  }
   140  
   141  type timerHeap []*entry
   142  
   143  func (h timerHeap) peek() *entry {
   144  	if h.Len() > 0 {
   145  		return h[0]
   146  	}
   147  	return nil
   148  }
   149  
   150  func (h *timerHeap) advanceTo(tnow time.Time) (out []func()) {
   151  	for {
   152  		if top := h.peek(); top != nil && !top.deadline.After(tnow) {
   153  			heap.Remove(h, top.index)
   154  			out = append(out, top.handler)
   155  		} else {
   156  			// There are no further expired timers.
   157  			return
   158  		}
   159  	}
   160  }
   161  
   162  func (h timerHeap) Len() int {
   163  	return len(h)
   164  }
   165  
   166  func (h timerHeap) Less(i, j int) bool {
   167  	return h[i].deadline.Before(h[j].deadline)
   168  }
   169  
   170  func (h timerHeap) Swap(i, j int) {
   171  	h[i], h[j] = h[j], h[i]
   172  	h[i].index = i
   173  	h[j].index = j
   174  }
   175  
   176  func (h *timerHeap) Push(x any) {
   177  	e := x.(*entry)
   178  	*h = append(*h, e)
   179  	e.index = len(*h) - 1
   180  }
   181  
   182  func (h *timerHeap) Pop() any {
   183  	n := h.Len()
   184  	e := (*h)[n-1]
   185  	*h = (*h)[:n-1]
   186  	e.index = -1
   187  	return e
   188  }