blob: 38f003d555f5bf3da3c40dd5448fbab004efccda [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:261/*
2 * Copyright 2014 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
Markus Handell3cb525b2020-07-16 14:16:0911#include "rtc_base/deprecated/recursive_critical_section.h"
Jonas Olssona4d87372019-07-05 17:08:3312
Yves Gerey3e707812018-11-28 15:47:4913#include <stddef.h>
14#include <stdint.h>
Jonas Olssona4d87372019-07-05 17:08:3315
jbauch555604a2016-04-26 10:13:2216#include <memory>
henrike@webrtc.orgf0488722014-05-13 18:00:2617#include <set>
Danil Chapovalov5740f3e2019-10-10 09:12:1518#include <type_traits>
Yves Gerey3e707812018-11-28 15:47:4919#include <utility>
henrike@webrtc.orgf0488722014-05-13 18:00:2620#include <vector>
21
Danil Chapovalov5740f3e2019-10-10 09:12:1522#include "absl/base/attributes.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3123#include "rtc_base/arraysize.h"
24#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3125#include "rtc_base/event.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3126#include "rtc_base/platform_thread.h"
27#include "rtc_base/thread.h"
Yves Gerey3e707812018-11-28 15:47:4928#include "test/gtest.h"
henrike@webrtc.orgf0488722014-05-13 18:00:2629
30namespace rtc {
31
32namespace {
33
Markus Handell2cfc1af2022-08-19 08:16:4834constexpr webrtc::TimeDelta kLongTime = webrtc::TimeDelta::Seconds(10);
35constexpr int kNumThreads = 16;
36constexpr int kOperationsToRun = 1000;
henrike@webrtc.orgf0488722014-05-13 18:00:2637
Jiayang Liubef8d2d2015-03-26 21:38:4638class UniqueValueVerifier {
henrike@webrtc.orgf0488722014-05-13 18:00:2639 public:
Jiayang Liubef8d2d2015-03-26 21:38:4640 void Verify(const std::vector<int>& values) {
41 for (size_t i = 0; i < values.size(); ++i) {
42 std::pair<std::set<int>::iterator, bool> result =
43 all_values_.insert(values[i]);
44 // Each value should only be taken by one thread, so if this value
45 // has already been added, something went wrong.
46 EXPECT_TRUE(result.second)
47 << " Thread=" << Thread::Current() << " value=" << values[i];
48 }
49 }
henrike@webrtc.orgf0488722014-05-13 18:00:2650
Jiayang Liubef8d2d2015-03-26 21:38:4651 void Finalize() {}
52
53 private:
54 std::set<int> all_values_;
55};
56
57class CompareAndSwapVerifier {
58 public:
59 CompareAndSwapVerifier() : zero_count_(0) {}
60
61 void Verify(const std::vector<int>& values) {
62 for (auto v : values) {
63 if (v == 0) {
64 EXPECT_EQ(0, zero_count_) << "Thread=" << Thread::Current();
65 ++zero_count_;
66 } else {
67 EXPECT_EQ(1, v) << " Thread=" << Thread::Current();
68 }
69 }
70 }
71
Yves Gerey665174f2018-06-19 13:03:0572 void Finalize() { EXPECT_EQ(1, zero_count_); }
73
Jiayang Liubef8d2d2015-03-26 21:38:4674 private:
75 int zero_count_;
76};
77
Danil Chapovalov1e6965a2022-09-05 09:27:5778class RunnerBase {
Jiayang Liubef8d2d2015-03-26 21:38:4679 public:
80 explicit RunnerBase(int value)
81 : threads_active_(0),
82 start_event_(true, false),
83 done_event_(true, false),
84 shared_value_(value) {}
henrike@webrtc.orgf0488722014-05-13 18:00:2685
86 bool Run() {
87 // Signal all threads to start.
88 start_event_.Set();
89
90 // Wait for all threads to finish.
91 return done_event_.Wait(kLongTime);
92 }
93
Niels Möller7a669002022-06-27 07:47:0294 void SetExpectedThreadCount(int count) { threads_active_.store(count); }
henrike@webrtc.orgf0488722014-05-13 18:00:2695
Jiayang Liubef8d2d2015-03-26 21:38:4696 int shared_value() const { return shared_value_; }
97
98 protected:
Yves Gerey665174f2018-06-19 13:03:0599 void BeforeStart() { ASSERT_TRUE(start_event_.Wait(kLongTime)); }
Jiayang Liubef8d2d2015-03-26 21:38:46100
101 // Returns true if all threads have finished.
102 bool AfterEnd() {
Niels Möller7a669002022-06-27 07:47:02103 if (threads_active_.fetch_sub(1) == 1) {
Jiayang Liubef8d2d2015-03-26 21:38:46104 done_event_.Set();
105 return true;
106 }
107 return false;
108 }
109
Niels Möller7a669002022-06-27 07:47:02110 std::atomic<int> threads_active_;
Jiayang Liubef8d2d2015-03-26 21:38:46111 Event start_event_;
112 Event done_event_;
113 int shared_value_;
114};
115
danilchap3c6abd22017-09-06 12:46:29116class RTC_LOCKABLE CriticalSectionLock {
Jiayang Liubef8d2d2015-03-26 21:38:46117 public:
danilchap3c6abd22017-09-06 12:46:29118 void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { cs_.Enter(); }
119 void Unlock() RTC_UNLOCK_FUNCTION() { cs_.Leave(); }
Jiayang Liubef8d2d2015-03-26 21:38:46120
121 private:
Markus Handell3cb525b2020-07-16 14:16:09122 RecursiveCriticalSection cs_;
Jiayang Liubef8d2d2015-03-26 21:38:46123};
124
125template <class Lock>
126class LockRunner : public RunnerBase {
127 public:
128 LockRunner() : RunnerBase(0) {}
129
Danil Chapovalov1e6965a2022-09-05 09:27:57130 void Loop() {
Jiayang Liubef8d2d2015-03-26 21:38:46131 BeforeStart();
132
133 lock_.Lock();
134
135 EXPECT_EQ(0, shared_value_);
136 int old = shared_value_;
137
138 // Use a loop to increase the chance of race.
139 for (int i = 0; i < kOperationsToRun; ++i) {
140 ++shared_value_;
141 }
142 EXPECT_EQ(old + kOperationsToRun, shared_value_);
143 shared_value_ = 0;
144
145 lock_.Unlock();
146
147 AfterEnd();
148 }
149
150 private:
151 Lock lock_;
152};
153
Danil Chapovalov1e6965a2022-09-05 09:27:57154template <typename Runner>
nisseb9c2f7c2017-04-20 09:23:08155void StartThreads(std::vector<std::unique_ptr<Thread>>* threads,
Danil Chapovalov1e6965a2022-09-05 09:27:57156 Runner* handler) {
henrike@webrtc.orgf0488722014-05-13 18:00:26157 for (int i = 0; i < kNumThreads; ++i) {
tommie7251592017-07-14 21:44:46158 std::unique_ptr<Thread> thread(Thread::Create());
henrike@webrtc.orgf0488722014-05-13 18:00:26159 thread->Start();
Danil Chapovalov1e6965a2022-09-05 09:27:57160 thread->PostTask([handler] { handler->Loop(); });
nisseb9c2f7c2017-04-20 09:23:08161 threads->push_back(std::move(thread));
henrike@webrtc.orgf0488722014-05-13 18:00:26162 }
163}
164
165} // namespace
166
Markus Handell3cb525b2020-07-16 14:16:09167TEST(RecursiveCriticalSectionTest, Basic) {
Jiayang Liubef8d2d2015-03-26 21:38:46168 // Create and start lots of threads.
169 LockRunner<CriticalSectionLock> runner;
nisseb9c2f7c2017-04-20 09:23:08170 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 21:38:46171 StartThreads(&threads, &runner);
172 runner.SetExpectedThreadCount(kNumThreads);
173
174 // Release the hounds!
175 EXPECT_TRUE(runner.Run());
176 EXPECT_EQ(0, runner.shared_value());
henrike@webrtc.orgf0488722014-05-13 18:00:26177}
178
tommied281e92016-01-22 07:47:25179class PerfTestData {
180 public:
181 PerfTestData(int expected_count, Event* event)
Yves Gerey665174f2018-06-19 13:03:05182 : cache_line_barrier_1_(),
183 cache_line_barrier_2_(),
184 expected_count_(expected_count),
185 event_(event) {
tommied281e92016-01-22 07:47:25186 cache_line_barrier_1_[0]++; // Avoid 'is not used'.
187 cache_line_barrier_2_[0]++; // Avoid 'is not used'.
188 }
189 ~PerfTestData() {}
190
191 void AddToCounter(int add) {
192 rtc::CritScope cs(&lock_);
193 my_counter_ += add;
194 if (my_counter_ == expected_count_)
195 event_->Set();
196 }
197
198 int64_t total() const {
199 // Assume that only one thread is running now.
200 return my_counter_;
201 }
202
203 private:
204 uint8_t cache_line_barrier_1_[64];
Markus Handell3cb525b2020-07-16 14:16:09205 RecursiveCriticalSection lock_;
tommied281e92016-01-22 07:47:25206 uint8_t cache_line_barrier_2_[64];
207 int64_t my_counter_ = 0;
208 const int expected_count_;
209 Event* const event_;
210};
211
212class PerfTestThread {
213 public:
tommied281e92016-01-22 07:47:25214 void Start(PerfTestData* data, int repeats, int id) {
tommied281e92016-01-22 07:47:25215 RTC_DCHECK(!data_);
216 data_ = data;
217 repeats_ = repeats;
218 my_id_ = id;
Markus Handellad5037b2021-05-07 13:02:36219 thread_ = PlatformThread::SpawnJoinable(
220 [this] {
221 for (int i = 0; i < repeats_; ++i)
222 data_->AddToCounter(my_id_);
223 },
224 "CsPerf");
tommied281e92016-01-22 07:47:25225 }
226
227 void Stop() {
tommied281e92016-01-22 07:47:25228 RTC_DCHECK(data_);
Markus Handellad5037b2021-05-07 13:02:36229 thread_.Finalize();
tommied281e92016-01-22 07:47:25230 repeats_ = 0;
231 data_ = nullptr;
232 my_id_ = 0;
233 }
234
235 private:
tommied281e92016-01-22 07:47:25236 PlatformThread thread_;
237 PerfTestData* data_ = nullptr;
238 int repeats_ = 0;
239 int my_id_ = 0;
240};
241
Oskar Sundbom13471a42019-03-01 15:11:49242// Comparison of output of this test as tested on a MacBook Pro, 13-inch,
243// 2017, 3,5 GHz Intel Core i7, 16 GB 2133 MHz LPDDR3,
244// running macOS Mojave, 10.14.3.
tommied281e92016-01-22 07:47:25245//
Oskar Sundbom13471a42019-03-01 15:11:49246// Native mutex implementation using fair policy (previously macOS default):
tommied281e92016-01-22 07:47:25247// Approximate CPU usage:
Oskar Sundbom13471a42019-03-01 15:11:49248// real 4m54.612s
249// user 1m20.575s
250// sys 3m48.872s
tommied281e92016-01-22 07:47:25251// Unit test output:
Markus Handell3cb525b2020-07-16 14:16:09252// [ OK ] RecursiveCriticalSectionTest.Performance (294375 ms)
Oskar Sundbom13471a42019-03-01 15:11:49253//
254// Native mutex implementation using first fit policy (current macOS default):
255// Approximate CPU usage:
256// real 0m11.535s
257// user 0m12.738s
258// sys 0m31.207s
259// Unit test output:
Markus Handell3cb525b2020-07-16 14:16:09260// [ OK ] RecursiveCriticalSectionTest.Performance (11444 ms)
tommied281e92016-01-22 07:47:25261//
262// Special partially spin lock based implementation:
263// Approximate CPU usage:
Oskar Sundbom13471a42019-03-01 15:11:49264// real 0m2.113s
265// user 0m3.014s
266// sys 0m4.495s
tommied281e92016-01-22 07:47:25267// Unit test output:
Markus Handell3cb525b2020-07-16 14:16:09268// [ OK ] RecursiveCriticalSectionTest.Performance (1885 ms)
tommied281e92016-01-22 07:47:25269//
270// The test is disabled by default to avoid unecessarily loading the bots.
Markus Handell3cb525b2020-07-16 14:16:09271TEST(RecursiveCriticalSectionTest, DISABLED_Performance) {
tommied281e92016-01-22 07:47:25272 PerfTestThread threads[8];
Niels Möllerc572ff32018-11-07 07:43:50273 Event event;
tommied281e92016-01-22 07:47:25274
275 static const int kThreadRepeats = 10000000;
276 static const int kExpectedCount = kThreadRepeats * arraysize(threads);
277 PerfTestData test_data(kExpectedCount, &event);
278
279 for (auto& t : threads)
280 t.Start(&test_data, kThreadRepeats, 1);
281
282 event.Wait(Event::kForever);
283
284 for (auto& t : threads)
285 t.Stop();
286}
287
henrike@webrtc.orgf0488722014-05-13 18:00:26288} // namespace rtc