summaryrefslogtreecommitdiff
path: root/node/RateLimiter.hpp
blob: e5403717f7da9f1d561fe6f79de90f5b7a888663 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * ZeroTier One - Global Peer to Peer Ethernet
 * Copyright (C) 2012-2013  ZeroTier Networks LLC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * --
 *
 * ZeroTier may be used and distributed under the terms of the GPLv3, which
 * are available at: http://www.gnu.org/licenses/gpl-3.0.html
 *
 * If you would like to embed ZeroTier into a commercial application or
 * redistribute it in a modified binary form, please contact ZeroTier Networks
 * LLC. Start here: http://www.zerotier.com/
 */

#ifndef _ZT_RATELIMITER_HPP
#define _ZT_RATELIMITER_HPP

#include <math.h>

#include "Constants.hpp"
#include "Utils.hpp"

#ifdef __WINDOWS__
#define fmin(a,b) (((a) <= (b)) ? (a) : (b))
#define fmax(a,b) (((a) >= (b)) ? (a) : (b))
#endif

namespace ZeroTier {

/**
 * Burstable rate limiter
 *
 * This limits a transfer rate to a maximum bytes per second using an
 * accounting method based on a balance rather than accumulating an
 * average rate. The result is a burstable rate limit rather than a
 * continuous rate limit; the link being limited may use all its balance
 * at once or slowly over time. Balance constantly replenishes over time
 * up to a configurable maximum balance.
 */
class RateLimiter
{
public:
	/**
	 * Limits to apply to a rate limiter
	 *
	 * Since many rate limiters may share the same fixed limit values,
	 * save memory by breaking this out into a struct parameter that
	 * can be passed into RateLimiter's methods.
	 */
	struct Limit
	{
		/**
		 * Speed in bytes per second, or rate of balance accrual
		 */
		double bytesPerSecond;

		/**
		 * Maximum balance that can ever be accrued (should be > 0.0)
		 */
		double maxBalance;

		/**
		 * Minimum balance, or maximum allowable "debt" (should be <= 0.0)
		 */
		double minBalance;
	};

	/**
	 * Create an uninitialized rate limiter
	 *
	 * init() must be called before this is used.
	 */
	RateLimiter() throw() {}

	/**
	 * @param preload Initial balance to place in account
	 */
	RateLimiter(double preload)
		throw()
	{
		init(preload);
	}

	/**
	 * Initialize or re-initialize rate limiter
	 *
	 * @param preload Initial balance to place in account
	 */
	inline void init(double preload)
		throw()
	{
		_lastTime = Utils::nowf();
		_balance = preload;
	}

	/**
	 * Update balance based on current clock and supplied Limits bytesPerSecond and maxBalance
	 *
	 * @param lim Current limits in effect
	 * @return New balance
	 */
	inline double updateBalance(const Limit &lim)
		throw()
	{
		double lt = _lastTime;
		double now = _lastTime = Utils::nowf();
		return (_balance = fmin(lim.maxBalance,_balance + (lim.bytesPerSecond * (now - lt))));
	}

	/**
	 * Update balance and test if a block of 'bytes' should be permitted to be transferred
	 *
	 * @param lim Current limits in effect
	 * @param bytes Number of bytes that we wish to transfer
	 * @return True if balance was sufficient
	 */
	inline bool gate(const Limit &lim,double bytes)
		throw()
	{
		bool allow = (updateBalance(lim) >= bytes);
		_balance = fmax(lim.minBalance,_balance - bytes);
		return allow;
	}

	/**
	 * @return Current balance
	 */
	inline double balance() const
		throw()
	{
		return _balance;
	}

private:
	double _lastTime;
	double _balance;
};

} // namespace ZeroTier

#endif