k8s.io/apiserver@v0.31.1/pkg/util/flowcontrol/fairqueuing/testing/eventclock/fake.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package eventclock
    18  
    19  import (
    20  	"container/heap"
    21  	"fmt"
    22  	"math/rand"
    23  	"runtime"
    24  	"strconv"
    25  	"strings"
    26  	"sync"
    27  	"time"
    28  
    29  	baseclocktest "k8s.io/utils/clock/testing"
    30  
    31  	"k8s.io/apiserver/pkg/util/flowcontrol/counter"
    32  	"k8s.io/apiserver/pkg/util/flowcontrol/fairqueuing/eventclock"
    33  	"k8s.io/klog/v2"
    34  )
    35  
    36  // waitGroupCounter is a wait group used for a GoRoutineCounter.  This private
    37  // type is used to disallow direct waitGroup access
    38  type waitGroupCounter struct {
    39  	wg sync.WaitGroup
    40  }
    41  
    42  // compile time assertion that waitGroupCounter meets requirements
    43  // of GoRoutineCounter
    44  var _ counter.GoRoutineCounter = (*waitGroupCounter)(nil)
    45  
    46  func (wgc *waitGroupCounter) Add(delta int) {
    47  	klogV := klog.V(7)
    48  	if klogV.Enabled() {
    49  		var pcs [10]uintptr
    50  		nCallers := runtime.Callers(2, pcs[:])
    51  		frames := runtime.CallersFrames(pcs[:nCallers])
    52  		callers := make(stackExcerpt, 0, 10)
    53  		more := frames != nil
    54  		boundary := 1
    55  		for i := 0; more && len(callers) < cap(callers); i++ {
    56  			var frame runtime.Frame
    57  			frame, more = frames.Next()
    58  			fileParts := strings.Split(frame.File, "/")
    59  			isMine := strings.HasSuffix(frame.File, "/fairqueuing/testing/eventclock/fake.go")
    60  			if isMine {
    61  				boundary = 2
    62  			}
    63  			callers = append(callers, stackFrame{file: fileParts[len(fileParts)-1], line: frame.Line})
    64  			if i >= boundary && !isMine {
    65  				break
    66  			}
    67  		}
    68  		klogV.InfoS("Add", "counter", fmt.Sprintf("%p", wgc), "delta", delta, "callers", callers)
    69  	}
    70  	wgc.wg.Add(delta)
    71  }
    72  
    73  type stackExcerpt []stackFrame
    74  
    75  type stackFrame struct {
    76  	file string
    77  	line int
    78  }
    79  
    80  var _ fmt.Stringer = stackExcerpt(nil)
    81  
    82  func (se stackExcerpt) String() string {
    83  	var sb strings.Builder
    84  	sb.WriteString("[")
    85  	for i, sf := range se {
    86  		if i > 0 {
    87  			sb.WriteString(", ")
    88  		}
    89  		sb.WriteString(sf.file)
    90  		sb.WriteString(":")
    91  		sb.WriteString(strconv.FormatInt(int64(sf.line), 10))
    92  	}
    93  	sb.WriteString("]")
    94  	return sb.String()
    95  }
    96  
    97  func (wgc *waitGroupCounter) Wait() {
    98  	wgc.wg.Wait()
    99  }
   100  
   101  // Fake is one whose time does not pass implicitly but
   102  // rather is explicitly set by invocations of its SetTime method.
   103  // Each Fake has an associated GoRoutineCounter that is
   104  // used to track associated activity.
   105  // For the EventAfterDuration and EventAfterTime methods,
   106  // the clock itself counts the start and stop of the EventFunc
   107  // and the client is responsible for counting any suspend and
   108  // resume internal to the EventFunc.
   109  // The Sleep method must only be invoked from a goroutine that is
   110  // counted in that GoRoutineCounter.
   111  type Fake struct {
   112  	baseclocktest.FakePassiveClock
   113  
   114  	// waiters is a heap of waiting work, sorted by time
   115  	waiters     eventWaiterHeap
   116  	waitersLock sync.RWMutex
   117  
   118  	// clientWG may be nil and if not supplies constraints on time
   119  	// passing in Run.  The Run method will not pick a new time until
   120  	// this is nil or its counter is zero.
   121  	clientWG *waitGroupCounter
   122  
   123  	// fuzz is the amount of noise to add to scheduling.  An event
   124  	// requested to run at time T will run at some time chosen
   125  	// uniformly at random from the interval [T, T+fuzz]; the upper
   126  	// bound is exclusive iff fuzz is non-zero.
   127  	fuzz time.Duration
   128  
   129  	// rand is the random number generator to use in fuzzing
   130  	rand *rand.Rand
   131  }
   132  
   133  var _ eventclock.Interface = &Fake{}
   134  
   135  type eventWaiterHeap []eventWaiter
   136  
   137  var _ heap.Interface = (*eventWaiterHeap)(nil)
   138  
   139  type eventWaiter struct {
   140  	targetTime time.Time
   141  	f          eventclock.EventFunc
   142  }
   143  
   144  // NewFake constructs a new fake event clock.  The given `r *rand.Rand` must
   145  // henceforth not be used for any other purpose.  If `r` is nil then a
   146  // fresh one will be constructed, seeded with the current real time.
   147  // The clientWG can be `nil` and if not is used to let Run know about
   148  // additional work that has to complete before time can advance.
   149  func NewFake(t time.Time, fuzz time.Duration, r *rand.Rand) (*Fake, counter.GoRoutineCounter) {
   150  	grc := &waitGroupCounter{}
   151  
   152  	if r == nil {
   153  		r = rand.New(rand.NewSource(time.Now().UnixNano()))
   154  		r.Uint64()
   155  		r.Uint64()
   156  		r.Uint64()
   157  	}
   158  	return &Fake{
   159  		FakePassiveClock: *baseclocktest.NewFakePassiveClock(t),
   160  		clientWG:         grc,
   161  		fuzz:             fuzz,
   162  		rand:             r,
   163  	}, grc
   164  }
   165  
   166  // GetNextTime returns the next time at which there is work scheduled,
   167  // and a bool indicating whether there is any such time
   168  func (fec *Fake) GetNextTime() (time.Time, bool) {
   169  	fec.waitersLock.RLock()
   170  	defer fec.waitersLock.RUnlock()
   171  	if len(fec.waiters) > 0 {
   172  		return fec.waiters[0].targetTime, true
   173  	}
   174  	return time.Time{}, false
   175  }
   176  
   177  // Run runs all the events scheduled, and all the events they
   178  // schedule, and so on, until there are none scheduled or the limit is not
   179  // nil and the next time would exceed the limit.  The associated
   180  // GoRoutineCounter gates the advancing of time.  That is,
   181  // time is not advanced until all the associated work is finished.
   182  func (fec *Fake) Run(limit *time.Time) {
   183  	for {
   184  		fec.clientWG.Wait()
   185  		t, ok := fec.GetNextTime()
   186  		if !ok || limit != nil && t.After(*limit) {
   187  			break
   188  		}
   189  		fec.SetTime(t)
   190  	}
   191  }
   192  
   193  // SetTime sets the time and runs to completion all events that should
   194  // be started by the given time --- including any further events they
   195  // schedule
   196  func (fec *Fake) SetTime(t time.Time) {
   197  	fec.FakePassiveClock.SetTime(t)
   198  	for {
   199  		foundSome := false
   200  		func() {
   201  			fec.waitersLock.Lock()
   202  			defer fec.waitersLock.Unlock()
   203  			// This loop is because events run at a given time may schedule more
   204  			// events to run at that or an earlier time.
   205  			// Events should not advance the clock.  But just in case they do...
   206  			now := fec.Now()
   207  			for len(fec.waiters) > 0 && !now.Before(fec.waiters[0].targetTime) {
   208  				ew := heap.Pop(&fec.waiters).(eventWaiter)
   209  				fec.clientWG.Add(1)
   210  				go func(f eventclock.EventFunc, now time.Time) {
   211  					f(now)
   212  					fec.clientWG.Add(-1)
   213  				}(ew.f, now)
   214  				foundSome = true
   215  			}
   216  		}()
   217  		if !foundSome {
   218  			break
   219  		}
   220  		fec.clientWG.Wait()
   221  	}
   222  }
   223  
   224  // Sleep returns after the given duration has passed.
   225  // Sleep must only be invoked in a goroutine that is counted
   226  // in the Fake's associated GoRoutineCounter.
   227  // Unlike the base FakeClock's Sleep, this method does not itself advance the clock
   228  // but rather leaves that up to other actors (e.g., Run).
   229  func (fec *Fake) Sleep(duration time.Duration) {
   230  	doneCh := make(chan struct{})
   231  	fec.EventAfterDuration(func(time.Time) {
   232  		fec.clientWG.Add(1)
   233  		close(doneCh)
   234  	}, duration)
   235  	fec.clientWG.Add(-1)
   236  	<-doneCh
   237  }
   238  
   239  // EventAfterDuration schedules the given function to be invoked once
   240  // the given duration has passed.
   241  func (fec *Fake) EventAfterDuration(f eventclock.EventFunc, d time.Duration) {
   242  	fec.waitersLock.Lock()
   243  	defer fec.waitersLock.Unlock()
   244  	now := fec.Now()
   245  	fd := time.Duration(float32(fec.fuzz) * fec.rand.Float32())
   246  	heap.Push(&fec.waiters, eventWaiter{targetTime: now.Add(d + fd), f: f})
   247  }
   248  
   249  // EventAfterTime schedules the given function to be invoked once
   250  // the given time has arrived.
   251  func (fec *Fake) EventAfterTime(f eventclock.EventFunc, t time.Time) {
   252  	fec.waitersLock.Lock()
   253  	defer fec.waitersLock.Unlock()
   254  	fd := time.Duration(float32(fec.fuzz) * fec.rand.Float32())
   255  	heap.Push(&fec.waiters, eventWaiter{targetTime: t.Add(fd), f: f})
   256  }
   257  
   258  func (ewh eventWaiterHeap) Len() int { return len(ewh) }
   259  
   260  func (ewh eventWaiterHeap) Less(i, j int) bool { return ewh[i].targetTime.Before(ewh[j].targetTime) }
   261  
   262  func (ewh eventWaiterHeap) Swap(i, j int) { ewh[i], ewh[j] = ewh[j], ewh[i] }
   263  
   264  func (ewh *eventWaiterHeap) Push(x interface{}) {
   265  	*ewh = append(*ewh, x.(eventWaiter))
   266  }
   267  
   268  func (ewh *eventWaiterHeap) Pop() interface{} {
   269  	old := *ewh
   270  	n := len(old)
   271  	x := old[n-1]
   272  	*ewh = old[:n-1]
   273  	return x
   274  }