blob: 6efea5458e1b087cfa205ce1e5e1b4f37165086f [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)
115 : rate_limiter_(rate_limiter),
116 start_signal_(false, false),
117 end_signal_(false, false) {}
118 virtual ~ThreadTask() {}
119
120 void Run() {
121 start_signal_.Wait(kMaxTimeoutMs);
122 DoRun();
123 end_signal_.Set();
124 }
125
126 virtual void DoRun() = 0;
127
128 RateLimiter* const rate_limiter_;
129 rtc::Event start_signal_;
130 rtc::Event end_signal_;
131};
132
tommi0f8b4032017-02-22 11:22:05 -0800133void RunTask(void* thread_task) {
sprangcd349d92016-07-13 09:11:28 -0700134 reinterpret_cast<ThreadTask*>(thread_task)->Run();
sprangcd349d92016-07-13 09:11:28 -0700135}
136
137TEST_F(RateLimitTest, MultiThreadedUsage) {
138 // Simple sanity test, with different threads calling the various methods.
139 // Runs a few simple tasks, each on its own thread, but coordinated with
140 // events so that they run in a serialized order. Intended to catch data
141 // races when run with tsan et al.
142
143 // Half window size, double rate -> same amount of bytes needed to fill rate.
144
145 class SetWindowSizeTask : public ThreadTask {
146 public:
147 explicit SetWindowSizeTask(RateLimiter* rate_limiter)
148 : ThreadTask(rate_limiter) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -0800149 ~SetWindowSizeTask() override {}
sprangcd349d92016-07-13 09:11:28 -0700150
151 void DoRun() override {
152 EXPECT_TRUE(rate_limiter_->SetWindowSize(kWindowSizeMs / 2));
153 }
154 } set_window_size_task(rate_limiter.get());
155 rtc::PlatformThread thread1(RunTask, &set_window_size_task, "Thread1");
156 thread1.Start();
157
158 class SetMaxRateTask : public ThreadTask {
159 public:
160 explicit SetMaxRateTask(RateLimiter* rate_limiter)
161 : ThreadTask(rate_limiter) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -0800162 ~SetMaxRateTask() override {}
sprangcd349d92016-07-13 09:11:28 -0700163
164 void DoRun() override { rate_limiter_->SetMaxRate(kMaxRateBps * 2); }
165 } set_max_rate_task(rate_limiter.get());
166 rtc::PlatformThread thread2(RunTask, &set_max_rate_task, "Thread2");
167 thread2.Start();
168
169 class UseRateTask : public ThreadTask {
170 public:
171 UseRateTask(RateLimiter* rate_limiter, SimulatedClock* clock)
172 : ThreadTask(rate_limiter), clock_(clock) {}
ehmaldonadoda8dcfb2017-01-04 07:11:23 -0800173 ~UseRateTask() override {}
sprangcd349d92016-07-13 09:11:28 -0700174
175 void DoRun() override {
176 EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
177 clock_->AdvanceTimeMilliseconds((kWindowSizeMs / 2) - 1);
178 EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
179 }
180
181 SimulatedClock* const clock_;
182 } use_rate_task(rate_limiter.get(), &clock_);
183 rtc::PlatformThread thread3(RunTask, &use_rate_task, "Thread3");
184 thread3.Start();
185
186 set_window_size_task.start_signal_.Set();
187 EXPECT_TRUE(set_window_size_task.end_signal_.Wait(kMaxTimeoutMs));
188
189 set_max_rate_task.start_signal_.Set();
190 EXPECT_TRUE(set_max_rate_task.end_signal_.Wait(kMaxTimeoutMs));
191
192 use_rate_task.start_signal_.Set();
193 EXPECT_TRUE(use_rate_task.end_signal_.Wait(kMaxTimeoutMs));
194
195 // All rate consumed.
196 EXPECT_FALSE(rate_limiter->TryUseRate(1));
197
198 thread1.Stop();
199 thread2.Stop();
200 thread3.Stop();
201}
202
203} // namespace webrtc