blob: 07dda5609eddebbb705eefef0ad609b524b496a8 [file] [log] [blame]
sprangcd349d92016-07-13 16:11:281/*
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
Jonas Olssona4d87372019-07-05 17:08:3311#include "rtc_base/rate_limiter.h"
12
sprangcd349d92016-07-13 16:11:2813#include <memory>
14
Mirko Bonadei92ea95e2017-09-15 04:47:3115#include "rtc_base/event.h"
16#include "rtc_base/platform_thread.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3117#include "system_wrappers/include/clock.h"
18#include "test/gtest.h"
sprangcd349d92016-07-13 16:11:2819
20namespace webrtc {
21
22class RateLimitTest : public ::testing::Test {
23 public:
24 RateLimitTest()
25 : clock_(0), rate_limiter(new RateLimiter(&clock_, kWindowSizeMs)) {}
ehmaldonadoda8dcfb2017-01-04 15:11:2326 ~RateLimitTest() override {}
sprangcd349d92016-07-13 16:11:2827
28 void SetUp() override { rate_limiter->SetMaxRate(kMaxRateBps); }
29
30 protected:
31 static constexpr int64_t kWindowSizeMs = 1000;
32 static constexpr uint32_t kMaxRateBps = 100000;
33 // Bytes needed to completely saturate the rate limiter.
34 static constexpr size_t kRateFillingBytes =
35 (kMaxRateBps * kWindowSizeMs) / (8 * 1000);
36 SimulatedClock clock_;
37 std::unique_ptr<RateLimiter> rate_limiter;
38};
39
40TEST_F(RateLimitTest, IncreasingMaxRate) {
41 // Fill rate, extend window to full size.
42 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
43 clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
44 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
45
46 // All rate consumed.
47 EXPECT_FALSE(rate_limiter->TryUseRate(1));
48
49 // Double the available rate and fill that too.
50 rate_limiter->SetMaxRate(kMaxRateBps * 2);
51 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes));
52
53 // All rate consumed again.
54 EXPECT_FALSE(rate_limiter->TryUseRate(1));
55}
56
57TEST_F(RateLimitTest, DecreasingMaxRate) {
58 // Fill rate, extend window to full size.
59 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
60 clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
61 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
62
63 // All rate consumed.
64 EXPECT_FALSE(rate_limiter->TryUseRate(1));
65
66 // Halve the available rate and move window so half of the data falls out.
67 rate_limiter->SetMaxRate(kMaxRateBps / 2);
68 clock_.AdvanceTimeMilliseconds(1);
69
70 // All rate still consumed.
71 EXPECT_FALSE(rate_limiter->TryUseRate(1));
72}
73
74TEST_F(RateLimitTest, ChangingWindowSize) {
75 // Fill rate, extend window to full size.
76 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
77 clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
78 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
79
80 // All rate consumed.
81 EXPECT_FALSE(rate_limiter->TryUseRate(1));
82
83 // Decrease window size so half of the data falls out.
84 rate_limiter->SetWindowSize(kWindowSizeMs / 2);
85 // Average rate should still be the same, so rate is still all consumed.
86 EXPECT_FALSE(rate_limiter->TryUseRate(1));
87
88 // Increase window size again. Now the rate is only half used (removed data
89 // points don't come back to life).
90 rate_limiter->SetWindowSize(kWindowSizeMs);
91 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
92
93 // All rate consumed again.
94 EXPECT_FALSE(rate_limiter->TryUseRate(1));
95}
96
97TEST_F(RateLimitTest, SingleUsageAlwaysOk) {
98 // Using more bytes than can fit in a window is OK for a single packet.
99 EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes + 1));
100}
101
102TEST_F(RateLimitTest, WindowSizeLimits) {
103 EXPECT_TRUE(rate_limiter->SetWindowSize(1));
104 EXPECT_FALSE(rate_limiter->SetWindowSize(0));
105 EXPECT_TRUE(rate_limiter->SetWindowSize(kWindowSizeMs));
106 EXPECT_FALSE(rate_limiter->SetWindowSize(kWindowSizeMs + 1));
107}
108
Markus Handell2cfc1af2022-08-19 08:16:48109static constexpr TimeDelta kMaxTimeout = TimeDelta::Seconds(30);
sprangcd349d92016-07-13 16:11:28110
111class ThreadTask {
112 public:
113 explicit ThreadTask(RateLimiter* rate_limiter)
Niels Möllerc572ff32018-11-07 07:43:50114 : rate_limiter_(rate_limiter) {}
sprangcd349d92016-07-13 16:11:28115 virtual ~ThreadTask() {}
116
117 void Run() {
Markus Handell2cfc1af2022-08-19 08:16:48118 start_signal_.Wait(kMaxTimeout);
sprangcd349d92016-07-13 16:11:28119 DoRun();
120 end_signal_.Set();
121 }
122
123 virtual void DoRun() = 0;
124
125 RateLimiter* const rate_limiter_;
126 rtc::Event start_signal_;
127 rtc::Event end_signal_;
128};
129
sprangcd349d92016-07-13 16:11:28130TEST_F(RateLimitTest, MultiThreadedUsage) {
131 // Simple sanity test, with different threads calling the various methods.
132 // Runs a few simple tasks, each on its own thread, but coordinated with
133 // events so that they run in a serialized order. Intended to catch data
134 // races when run with tsan et al.
135
136 // Half window size, double rate -> same amount of bytes needed to fill rate.
137
138 class SetWindowSizeTask : public ThreadTask {
139 public:
140 explicit SetWindowSizeTask(RateLimiter* rate_limiter)
141 : ThreadTask(rate_limiter) {}
ehmaldonadoda8dcfb2017-01-04 15:11:23142 ~SetWindowSizeTask() override {}
sprangcd349d92016-07-13 16:11:28143
144 void DoRun() override {
145 EXPECT_TRUE(rate_limiter_->SetWindowSize(kWindowSizeMs / 2));
146 }
147 } set_window_size_task(rate_limiter.get());
Markus Handellad5037b2021-05-07 13:02:36148 auto thread1 = rtc::PlatformThread::SpawnJoinable(
149 [&set_window_size_task] { set_window_size_task.Run(); }, "Thread1");
sprangcd349d92016-07-13 16:11:28150
151 class SetMaxRateTask : public ThreadTask {
152 public:
153 explicit SetMaxRateTask(RateLimiter* rate_limiter)
154 : ThreadTask(rate_limiter) {}
ehmaldonadoda8dcfb2017-01-04 15:11:23155 ~SetMaxRateTask() override {}
sprangcd349d92016-07-13 16:11:28156
157 void DoRun() override { rate_limiter_->SetMaxRate(kMaxRateBps * 2); }
158 } set_max_rate_task(rate_limiter.get());
Markus Handellad5037b2021-05-07 13:02:36159 auto thread2 = rtc::PlatformThread::SpawnJoinable(
160 [&set_max_rate_task] { set_max_rate_task.Run(); }, "Thread2");
sprangcd349d92016-07-13 16:11:28161
162 class UseRateTask : public ThreadTask {
163 public:
164 UseRateTask(RateLimiter* rate_limiter, SimulatedClock* clock)
165 : ThreadTask(rate_limiter), clock_(clock) {}
ehmaldonadoda8dcfb2017-01-04 15:11:23166 ~UseRateTask() override {}
sprangcd349d92016-07-13 16:11:28167
168 void DoRun() override {
169 EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
170 clock_->AdvanceTimeMilliseconds((kWindowSizeMs / 2) - 1);
171 EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
172 }
173
174 SimulatedClock* const clock_;
175 } use_rate_task(rate_limiter.get(), &clock_);
Markus Handellad5037b2021-05-07 13:02:36176 auto thread3 = rtc::PlatformThread::SpawnJoinable(
177 [&use_rate_task] { use_rate_task.Run(); }, "Thread3");
sprangcd349d92016-07-13 16:11:28178
179 set_window_size_task.start_signal_.Set();
Markus Handell2cfc1af2022-08-19 08:16:48180 EXPECT_TRUE(set_window_size_task.end_signal_.Wait(kMaxTimeout));
sprangcd349d92016-07-13 16:11:28181
182 set_max_rate_task.start_signal_.Set();
Markus Handell2cfc1af2022-08-19 08:16:48183 EXPECT_TRUE(set_max_rate_task.end_signal_.Wait(kMaxTimeout));
sprangcd349d92016-07-13 16:11:28184
185 use_rate_task.start_signal_.Set();
Markus Handell2cfc1af2022-08-19 08:16:48186 EXPECT_TRUE(use_rate_task.end_signal_.Wait(kMaxTimeout));
sprangcd349d92016-07-13 16:11:28187
188 // All rate consumed.
189 EXPECT_FALSE(rate_limiter->TryUseRate(1));
sprangcd349d92016-07-13 16:11:28190}
191
192} // namespace webrtc