github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/timer.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  import (
     7  	"time"
     8  )
     9  
    10  // SimpleTimer keeps track of how long something is taking, like a network
    11  // API call. Meant to be very simple.  It is not meant to be goroutine safe
    12  // and should only be called from one Goroutine.
    13  type SimpleTimer struct {
    14  	total time.Duration
    15  	start *time.Time
    16  }
    17  
    18  // Start the timer running, if it's not already started.
    19  func (s *SimpleTimer) Start() {
    20  	if s.start == nil {
    21  		tmp := time.Now()
    22  		s.start = &tmp
    23  	}
    24  }
    25  
    26  // Stop the timer; panic if it hasn't been previously started.  Return
    27  // the total duration spent in the timer.
    28  func (s *SimpleTimer) Stop() time.Duration {
    29  	if s.start != nil {
    30  		s.total += time.Since(*s.start)
    31  	} else {
    32  		panic("SimpleTimer Stop()'ed without being started")
    33  	}
    34  	return s.GetTotal()
    35  }
    36  
    37  // GetTotal gets the total duration spent in the timer.
    38  func (s *SimpleTimer) GetTotal() time.Duration {
    39  	return s.total
    40  }
    41  
    42  // Reset the internal duration counter.
    43  func (s *SimpleTimer) Reset() {
    44  	s.total = 0
    45  }
    46  
    47  // ReportingTimer is an interface shared between ReportingTimerReal
    48  // and ReportingTimerDummy, to allow for convenient disabling of timer features.
    49  type ReportingTimer interface {
    50  	Report(prefix string)
    51  }
    52  
    53  // ReportingTimerReal is a SimpleTimer that reports after the timing measurement
    54  // is done.
    55  type ReportingTimerReal struct {
    56  	SimpleTimer
    57  	Contextified
    58  }
    59  
    60  // NewReportingTimerReal returns an initialized reporting timer that
    61  // actually reports timing information.
    62  func NewReportingTimerReal(ctx Contextified) *ReportingTimerReal {
    63  	return &ReportingTimerReal{Contextified: ctx}
    64  }
    65  
    66  // Report stops and resets the timer, then logs to Info what the duration was.
    67  func (r *ReportingTimerReal) Report(prefix string) {
    68  	dur := r.Stop()
    69  	r.Reset()
    70  	r.G().Log.Info("timer: %s [%d ms]", prefix, dur/time.Millisecond)
    71  }
    72  
    73  // ReportingTimerDummy fulfills the ReportingTimer interface but doesn't
    74  // do anything when done.
    75  type ReportingTimerDummy struct{}
    76  
    77  // Report is a noop.
    78  func (r ReportingTimerDummy) Report(prefix string) {}
    79  
    80  // TimerSelector allows us to select which timers we want on.
    81  type TimerSelector int
    82  
    83  const (
    84  	// TimerNone means Timers Disabled
    85  	TimerNone TimerSelector = 0
    86  	// TimerAPI enables API timers
    87  	TimerAPI TimerSelector = 1 << iota
    88  	// TimerXAPI enables External API timers
    89  	TimerXAPI
    90  	// TimerRPC enables RPC timers
    91  	TimerRPC
    92  )
    93  
    94  // TimerSet is the set of currently active timers
    95  type TimerSet struct {
    96  	sel TimerSelector
    97  	Contextified
    98  }
    99  
   100  // NewTimerSet looks into the given context for configuration information
   101  // about how to set up timers.  It then returns the corresponding TimerSet.
   102  func NewTimerSet(g *GlobalContext) *TimerSet {
   103  	s := g.Env.GetTimers()
   104  	sel := TimerNone
   105  	for _, c := range s {
   106  		switch c {
   107  		case 'a':
   108  			sel |= TimerAPI
   109  		case 'x':
   110  			sel |= TimerXAPI
   111  		case 'r':
   112  			sel |= TimerRPC
   113  
   114  		}
   115  	}
   116  	return &TimerSet{sel: sel, Contextified: Contextified{g}}
   117  }
   118  
   119  // Start allocates and starts a new timer if the passed TimerSelector
   120  // is currently enabled.  Otherwise, it just returns a Dummy timer.
   121  func (s TimerSet) Start(sel TimerSelector) ReportingTimer {
   122  	var ret ReportingTimer
   123  	if s.sel&sel == sel {
   124  		tmp := NewReportingTimerReal(s.Contextified)
   125  		tmp.Start()
   126  		ret = tmp
   127  	} else {
   128  		ret = ReportingTimerDummy{}
   129  	}
   130  	return ret
   131  }