1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package libkb
5
6import (
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.
13type SimpleTimer struct {
14	total time.Duration
15	start *time.Time
16}
17
18// Start the timer running, if it's not already started.
19func (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.
28func (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.
38func (s *SimpleTimer) GetTotal() time.Duration {
39	return s.total
40}
41
42// Reset the internal duration counter.
43func (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.
49type ReportingTimer interface {
50	Report(prefix string)
51}
52
53// ReportingTimerReal is a SimpleTimer that reports after the timing measurement
54// is done.
55type ReportingTimerReal struct {
56	SimpleTimer
57	Contextified
58}
59
60// NewReportingTimerReal returns an initialized reporting timer that
61// actually reports timing information.
62func 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.
67func (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.
75type ReportingTimerDummy struct{}
76
77// Report is a noop.
78func (r ReportingTimerDummy) Report(prefix string) {}
79
80// TimerSelector allows us to select which timers we want on.
81type TimerSelector int
82
83const (
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
95type 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.
102func 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.
121func (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}
132