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