blob: cb9b5b48307843b1f11a614ee80332fbb364ae1f [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
sprangcd349d92016-07-13 09:11:28 -070011#include <memory>
12
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "rtc_base/event.h"
14#include "rtc_base/platform_thread.h"
15#include "rtc_base/rate_limiter.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "system_wrappers/include/clock.h"
17#include "test/gtest.h"
sprangcd349d92016-07-13 09:11:28 -070018
19namespace webrtc {
20
21class RateLimitTest : public ::testing::Test {
22 public:
23 RateLimitTest()
24 : clock_(0), rate_limiter(new RateLimiter(&clock_, kWindowSizeMs)) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -080025 ~RateLimitTest() override {}
sprangcd349d92016-07-13 09:11:28 -070026
27 void SetUp() override { rate_limiter->SetMaxRate(kMaxRateBps); }
28
29 protected:
30 static constexpr int64_t kWindowSizeMs = 1000;
31 static constexpr uint32_t kMaxRateBps = 100000;
32 // Bytes needed to completely saturate the rate limiter.
33 static constexpr size_t kRateFillingBytes =
34 (kMaxRateBps * kWindowSizeMs) / (8 * 1000);
35 SimulatedClock clock_;
36 std::unique_ptr<RateLimiter> rate_limiter;
37};
38
39TEST_F(RateLimitTest, IncreasingMaxRate) {
40 // Fill rate, extend window to full size.
41 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
42 clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
43 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
44
45 // All rate consumed.
46 EXPECT_FALSE(rate_limiter->TryUseRate(1));
47
48 // Double the available rate and fill that too.
49 rate_limiter->SetMaxRate(kMaxRateBps * 2);
50 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes));
51
52 // All rate consumed again.
53 EXPECT_FALSE(rate_limiter->TryUseRate(1));
54}
55
56TEST_F(RateLimitTest, DecreasingMaxRate) {
57 // Fill rate, extend window to full size.
58 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
59 clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
60 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
61
62 // All rate consumed.
63 EXPECT_FALSE(rate_limiter->TryUseRate(1));
64
65 // Halve the available rate and move window so half of the data falls out.
66 rate_limiter->SetMaxRate(kMaxRateBps / 2);
67 clock_.AdvanceTimeMilliseconds(1);
68
69 // All rate still consumed.
70 EXPECT_FALSE(rate_limiter->TryUseRate(1));
71}
72
73TEST_F(RateLimitTest, ChangingWindowSize) {
74 // Fill rate, extend window to full size.
75 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
76 clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
77 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
78
79 // All rate consumed.
80 EXPECT_FALSE(rate_limiter->TryUseRate(1));
81
82 // Decrease window size so half of the data falls out.
83 rate_limiter->SetWindowSize(kWindowSizeMs / 2);
84 // Average rate should still be the same, so rate is still all consumed.
85 EXPECT_FALSE(rate_limiter->TryUseRate(1));
86
87 // Increase window size again. Now the rate is only half used (removed data
88 // points don't come back to life).
89 rate_limiter->SetWindowSize(kWindowSizeMs);
90 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
91
92 // All rate consumed again.
93 EXPECT_FALSE(rate_limiter->TryUseRate(1));
94}
95
96TEST_F(RateLimitTest, SingleUsageAlwaysOk) {
97 // Using more bytes than can fit in a window is OK for a single packet.
98 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes + 1));
99}
100
101TEST_F(RateLimitTest, WindowSizeLimits) {
102 EXPECT_TRUE(rate_limiter->SetWindowSize(1));
103 EXPECT_FALSE(rate_limiter->SetWindowSize(0));
104 EXPECT_TRUE(rate_limiter->SetWindowSize(kWindowSizeMs));
105 EXPECT_FALSE(rate_limiter->SetWindowSize(kWindowSizeMs + 1));
106}
107
108static const int64_t kMaxTimeoutMs = 30000;
109
110class ThreadTask {
111 public:
112 explicit ThreadTask(RateLimiter* rate_limiter)
Niels Möllerc572ff32018-11-07 08:43:50 +0100113 : rate_limiter_(rate_limiter) {}
sprangcd349d92016-07-13 09:11:28 -0700114 virtual ~ThreadTask() {}
115
116 void Run() {
117 start_signal_.Wait(kMaxTimeoutMs);
118 DoRun();
119 end_signal_.Set();
120 }
121
122 virtual void DoRun() = 0;
123
124 RateLimiter* const rate_limiter_;
125 rtc::Event start_signal_;
126 rtc::Event end_signal_;
127};
128
tommi0f8b4032017-02-22 11:22:05 -0800129void RunTask(void* thread_task) {
sprangcd349d92016-07-13 09:11:28 -0700130 reinterpret_cast<ThreadTask*>(thread_task)->Run();
sprangcd349d92016-07-13 09:11:28 -0700131}
132
133TEST_F(RateLimitTest, MultiThreadedUsage) {
134 // Simple sanity test, with different threads calling the various methods.
135 // Runs a few simple tasks, each on its own thread, but coordinated with
136 // events so that they run in a serialized order. Intended to catch data
137 // races when run with tsan et al.
138
139 // Half window size, double rate -> same amount of bytes needed to fill rate.
140
141 class SetWindowSizeTask : public ThreadTask {
142 public:
143 explicit SetWindowSizeTask(RateLimiter* rate_limiter)
144 : ThreadTask(rate_limiter) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -0800145 ~SetWindowSizeTask() override {}
sprangcd349d92016-07-13 09:11:28 -0700146
147 void DoRun() override {
148 EXPECT_TRUE(rate_limiter_->SetWindowSize(kWindowSizeMs / 2));
149 }
150 } set_window_size_task(rate_limiter.get());
151 rtc::PlatformThread thread1(RunTask, &set_window_size_task, "Thread1");
152 thread1.Start();
153
154 class SetMaxRateTask : public ThreadTask {
155 public:
156 explicit SetMaxRateTask(RateLimiter* rate_limiter)
157 : ThreadTask(rate_limiter) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -0800158 ~SetMaxRateTask() override {}
sprangcd349d92016-07-13 09:11:28 -0700159
160 void DoRun() override { rate_limiter_->SetMaxRate(kMaxRateBps * 2); }
161 } set_max_rate_task(rate_limiter.get());
162 rtc::PlatformThread thread2(RunTask, &set_max_rate_task, "Thread2");
163 thread2.Start();
164
165 class UseRateTask : public ThreadTask {
166 public:
167 UseRateTask(RateLimiter* rate_limiter, SimulatedClock* clock)
168 : ThreadTask(rate_limiter), clock_(clock) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -0800169 ~UseRateTask() override {}
sprangcd349d92016-07-13 09:11:28 -0700170
171 void DoRun() override {
172 EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
173 clock_->AdvanceTimeMilliseconds((kWindowSizeMs / 2) - 1);
174 EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
175 }
176
177 SimulatedClock* const clock_;
178 } use_rate_task(rate_limiter.get(), &clock_);
179 rtc::PlatformThread thread3(RunTask, &use_rate_task, "Thread3");
180 thread3.Start();
181
182 set_window_size_task.start_signal_.Set();
183 EXPECT_TRUE(set_window_size_task.end_signal_.Wait(kMaxTimeoutMs));
184
185 set_max_rate_task.start_signal_.Set();
186 EXPECT_TRUE(set_max_rate_task.end_signal_.Wait(kMaxTimeoutMs));
187
188 use_rate_task.start_signal_.Set();
189 EXPECT_TRUE(use_rate_task.end_signal_.Wait(kMaxTimeoutMs));
190
191 // All rate consumed.
192 EXPECT_FALSE(rate_limiter->TryUseRate(1));
193
194 thread1.Stop();
195 thread2.Stop();
196 thread3.Stop();
197}
198
199} // namespace webrtc