blob: e619ec66054d6b9b12c72fc646e35a140558e2a9 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package storage
18
19import (
20 "sync"
21 "time"
22)
23
24const (
25 refreshPerSecond = 50 * time.Millisecond
26 maxBudget = 100 * time.Millisecond
27)
28
29// timeBudget implements a budget of time that you can use and is
30// periodically being refreshed. The pattern to use it is:
31// budget := newTimeBudget(...)
32// ...
33// timeout := budget.takeAvailable()
34// // Now you can spend at most timeout on doing stuff
35// ...
36// // If you didn't use all timeout, return what you didn't use
37// budget.returnUnused(<unused part of timeout>)
38//
39// NOTE: It's not recommended to be used concurrently from multiple threads -
40// if first user takes the whole timeout, the second one will get 0 timeout
41// even though the first one may return something later.
42type timeBudget struct {
43 sync.Mutex
44 budget time.Duration
45
46 refresh time.Duration
47 maxBudget time.Duration
48}
49
50func newTimeBudget(stopCh <-chan struct{}) *timeBudget {
51 result := &timeBudget{
52 budget: time.Duration(0),
53 refresh: refreshPerSecond,
54 maxBudget: maxBudget,
55 }
56 go result.periodicallyRefresh(stopCh)
57 return result
58}
59
60func (t *timeBudget) periodicallyRefresh(stopCh <-chan struct{}) {
61 ticker := time.NewTicker(time.Second)
62 defer ticker.Stop()
63 for {
64 select {
65 case <-ticker.C:
66 t.Lock()
67 if t.budget = t.budget + t.refresh; t.budget > t.maxBudget {
68 t.budget = t.maxBudget
69 }
70 t.Unlock()
71 case <-stopCh:
72 return
73 }
74 }
75}
76
77func (t *timeBudget) takeAvailable() time.Duration {
78 t.Lock()
79 defer t.Unlock()
80 result := t.budget
81 t.budget = time.Duration(0)
82 return result
83}
84
85func (t *timeBudget) returnUnused(unused time.Duration) {
86 t.Lock()
87 defer t.Unlock()
88 if unused < 0 {
89 // We used more than allowed.
90 return
91 }
92 if t.budget = t.budget + unused; t.budget > t.maxBudget {
93 t.budget = t.maxBudget
94 }
95}