blob: ac0625fa186f79ceb17cdc242648a07ebe919e3d [file] [log] [blame]
sprangcd349d92016-07-13 09:11:28 -07001/*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <algorithm>
12#include <memory>
13
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020014#include "rtc_base/event.h"
15#include "rtc_base/platform_thread.h"
16#include "rtc_base/rate_limiter.h"
17#include "rtc_base/task_queue.h"
18#include "system_wrappers/include/clock.h"
19#include "test/gtest.h"
sprangcd349d92016-07-13 09:11:28 -070020
21namespace webrtc {
22
23class RateLimitTest : public ::testing::Test {
24 public:
25 RateLimitTest()
26 : clock_(0), rate_limiter(new RateLimiter(&clock_, kWindowSizeMs)) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -080027 ~RateLimitTest() override {}
sprangcd349d92016-07-13 09:11:28 -070028
29 void SetUp() override { rate_limiter->SetMaxRate(kMaxRateBps); }
30
31 protected:
32 static constexpr int64_t kWindowSizeMs = 1000;
33 static constexpr uint32_t kMaxRateBps = 100000;
34 // Bytes needed to completely saturate the rate limiter.
35 static constexpr size_t kRateFillingBytes =
36 (kMaxRateBps * kWindowSizeMs) / (8 * 1000);
37 SimulatedClock clock_;
38 std::unique_ptr<RateLimiter> rate_limiter;
39};
40
41TEST_F(RateLimitTest, IncreasingMaxRate) {
42 // Fill rate, extend window to full size.
43 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
44 clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
45 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
46
47 // All rate consumed.
48 EXPECT_FALSE(rate_limiter->TryUseRate(1));
49
50 // Double the available rate and fill that too.
51 rate_limiter->SetMaxRate(kMaxRateBps * 2);
52 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes));
53
54 // All rate consumed again.
55 EXPECT_FALSE(rate_limiter->TryUseRate(1));
56}
57
58TEST_F(RateLimitTest, DecreasingMaxRate) {
59 // Fill rate, extend window to full size.
60 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
61 clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
62 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
63
64 // All rate consumed.
65 EXPECT_FALSE(rate_limiter->TryUseRate(1));
66
67 // Halve the available rate and move window so half of the data falls out.
68 rate_limiter->SetMaxRate(kMaxRateBps / 2);
69 clock_.AdvanceTimeMilliseconds(1);
70
71 // All rate still consumed.
72 EXPECT_FALSE(rate_limiter->TryUseRate(1));
73}
74
75TEST_F(RateLimitTest, ChangingWindowSize) {
76 // Fill rate, extend window to full size.
77 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
78 clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
79 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
80
81 // All rate consumed.
82 EXPECT_FALSE(rate_limiter->TryUseRate(1));
83
84 // Decrease window size so half of the data falls out.
85 rate_limiter->SetWindowSize(kWindowSizeMs / 2);
86 // Average rate should still be the same, so rate is still all consumed.
87 EXPECT_FALSE(rate_limiter->TryUseRate(1));
88
89 // Increase window size again. Now the rate is only half used (removed data
90 // points don't come back to life).
91 rate_limiter->SetWindowSize(kWindowSizeMs);
92 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
93
94 // All rate consumed again.
95 EXPECT_FALSE(rate_limiter->TryUseRate(1));
96}
97
98TEST_F(RateLimitTest, SingleUsageAlwaysOk) {
99 // Using more bytes than can fit in a window is OK for a single packet.
100 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes + 1));
101}
102
103TEST_F(RateLimitTest, WindowSizeLimits) {
104 EXPECT_TRUE(rate_limiter->SetWindowSize(1));
105 EXPECT_FALSE(rate_limiter->SetWindowSize(0));
106 EXPECT_TRUE(rate_limiter->SetWindowSize(kWindowSizeMs));
107 EXPECT_FALSE(rate_limiter->SetWindowSize(kWindowSizeMs + 1));
108}
109
110static const int64_t kMaxTimeoutMs = 30000;
111
112class ThreadTask {
113 public:
114 explicit ThreadTask(RateLimiter* rate_limiter)
Niels Möllerc572ff32018-11-07 08:43:50 +0100115 : rate_limiter_(rate_limiter) {}
sprangcd349d92016-07-13 09:11:28 -0700116 virtual ~ThreadTask() {}
117
118 void Run() {
119 start_signal_.Wait(kMaxTimeoutMs);
120 DoRun();
121 end_signal_.Set();
122 }
123
124 virtual void DoRun() = 0;
125
126 RateLimiter* const rate_limiter_;
127 rtc::Event start_signal_;
128 rtc::Event end_signal_;
129};
130
tommi0f8b4032017-02-22 11:22:05 -0800131void RunTask(void* thread_task) {
sprangcd349d92016-07-13 09:11:28 -0700132 reinterpret_cast<ThreadTask*>(thread_task)->Run();
sprangcd349d92016-07-13 09:11:28 -0700133}
134
135TEST_F(RateLimitTest, MultiThreadedUsage) {
136 // Simple sanity test, with different threads calling the various methods.
137 // Runs a few simple tasks, each on its own thread, but coordinated with
138 // events so that they run in a serialized order. Intended to catch data
139 // races when run with tsan et al.
140
141 // Half window size, double rate -> same amount of bytes needed to fill rate.
142
143 class SetWindowSizeTask : public ThreadTask {
144 public:
145 explicit SetWindowSizeTask(RateLimiter* rate_limiter)
146 : ThreadTask(rate_limiter) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -0800147 ~SetWindowSizeTask() override {}
sprangcd349d92016-07-13 09:11:28 -0700148
149 void DoRun() override {
150 EXPECT_TRUE(rate_limiter_->SetWindowSize(kWindowSizeMs / 2));
151 }
152 } set_window_size_task(rate_limiter.get());
153 rtc::PlatformThread thread1(RunTask, &set_window_size_task, "Thread1");
154 thread1.Start();
155
156 class SetMaxRateTask : public ThreadTask {
157 public:
158 explicit SetMaxRateTask(RateLimiter* rate_limiter)
159 : ThreadTask(rate_limiter) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -0800160 ~SetMaxRateTask() override {}
sprangcd349d92016-07-13 09:11:28 -0700161
162 void DoRun() override { rate_limiter_->SetMaxRate(kMaxRateBps * 2); }
163 } set_max_rate_task(rate_limiter.get());
164 rtc::PlatformThread thread2(RunTask, &set_max_rate_task, "Thread2");
165 thread2.Start();
166
167 class UseRateTask : public ThreadTask {
168 public:
169 UseRateTask(RateLimiter* rate_limiter, SimulatedClock* clock)
170 : ThreadTask(rate_limiter), clock_(clock) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -0800171 ~UseRateTask() override {}
sprangcd349d92016-07-13 09:11:28 -0700172
173 void DoRun() override {
174 EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
175 clock_->AdvanceTimeMilliseconds((kWindowSizeMs / 2) - 1);
176 EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
177 }
178
179 SimulatedClock* const clock_;
180 } use_rate_task(rate_limiter.get(), &clock_);
181 rtc::PlatformThread thread3(RunTask, &use_rate_task, "Thread3");
182 thread3.Start();
183
184 set_window_size_task.start_signal_.Set();
185 EXPECT_TRUE(set_window_size_task.end_signal_.Wait(kMaxTimeoutMs));
186
187 set_max_rate_task.start_signal_.Set();
188 EXPECT_TRUE(set_max_rate_task.end_signal_.Wait(kMaxTimeoutMs));
189
190 use_rate_task.start_signal_.Set();
191 EXPECT_TRUE(use_rate_task.end_signal_.Wait(kMaxTimeoutMs));
192
193 // All rate consumed.
194 EXPECT_FALSE(rate_limiter->TryUseRate(1));
195
196 thread1.Stop();
197 thread2.Stop();
198 thread3.Stop();
199}
200
201} // namespace webrtc