blob: 16aefd27400212ac54c31c96ebb9924ff17485a1 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
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
Jonas Olssona4d87372019-07-05 19:08:33 +020011#include "rtc_base/critical_section.h"
12
Yves Gerey3e707812018-11-28 16:47:49 +010013#include <stddef.h>
14#include <stdint.h>
Jonas Olssona4d87372019-07-05 19:08:33 +020015
jbauch555604a2016-04-26 03:13:22 -070016#include <memory>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000017#include <set>
Danil Chapovalov5740f3e2019-10-10 11:12:15 +020018#include <type_traits>
Yves Gerey3e707812018-11-28 16:47:49 +010019#include <utility>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000020#include <vector>
21
Danil Chapovalov5740f3e2019-10-10 11:12:15 +020022#include "absl/base/attributes.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "rtc_base/arraysize.h"
Steve Anton10542f22019-01-11 09:11:00 -080024#include "rtc_base/atomic_ops.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026#include "rtc_base/event.h"
Yves Gerey3e707812018-11-28 16:47:49 +010027#include "rtc_base/location.h"
Steve Anton10542f22019-01-11 09:11:00 -080028#include "rtc_base/message_handler.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "rtc_base/platform_thread.h"
30#include "rtc_base/thread.h"
Yves Gerey3e707812018-11-28 16:47:49 +010031#include "test/gtest.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000032
33namespace rtc {
34
35namespace {
36
37const int kLongTime = 10000; // 10 seconds
38const int kNumThreads = 16;
39const int kOperationsToRun = 1000;
40
Jiayang Liubef8d2d2015-03-26 14:38:46 -070041class UniqueValueVerifier {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000042 public:
Jiayang Liubef8d2d2015-03-26 14:38:46 -070043 void Verify(const std::vector<int>& values) {
44 for (size_t i = 0; i < values.size(); ++i) {
45 std::pair<std::set<int>::iterator, bool> result =
46 all_values_.insert(values[i]);
47 // Each value should only be taken by one thread, so if this value
48 // has already been added, something went wrong.
49 EXPECT_TRUE(result.second)
50 << " Thread=" << Thread::Current() << " value=" << values[i];
51 }
52 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000053
Jiayang Liubef8d2d2015-03-26 14:38:46 -070054 void Finalize() {}
55
56 private:
57 std::set<int> all_values_;
58};
59
60class CompareAndSwapVerifier {
61 public:
62 CompareAndSwapVerifier() : zero_count_(0) {}
63
64 void Verify(const std::vector<int>& values) {
65 for (auto v : values) {
66 if (v == 0) {
67 EXPECT_EQ(0, zero_count_) << "Thread=" << Thread::Current();
68 ++zero_count_;
69 } else {
70 EXPECT_EQ(1, v) << " Thread=" << Thread::Current();
71 }
72 }
73 }
74
Yves Gerey665174f2018-06-19 15:03:05 +020075 void Finalize() { EXPECT_EQ(1, zero_count_); }
76
Jiayang Liubef8d2d2015-03-26 14:38:46 -070077 private:
78 int zero_count_;
79};
80
81class RunnerBase : public MessageHandler {
82 public:
83 explicit RunnerBase(int value)
84 : threads_active_(0),
85 start_event_(true, false),
86 done_event_(true, false),
87 shared_value_(value) {}
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000088
89 bool Run() {
90 // Signal all threads to start.
91 start_event_.Set();
92
93 // Wait for all threads to finish.
94 return done_event_.Wait(kLongTime);
95 }
96
Yves Gerey665174f2018-06-19 15:03:05 +020097 void SetExpectedThreadCount(int count) { threads_active_ = count; }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000098
Jiayang Liubef8d2d2015-03-26 14:38:46 -070099 int shared_value() const { return shared_value_; }
100
101 protected:
102 // Derived classes must override OnMessage, and call BeforeStart and AfterEnd
103 // at the beginning and the end of OnMessage respectively.
Yves Gerey665174f2018-06-19 15:03:05 +0200104 void BeforeStart() { ASSERT_TRUE(start_event_.Wait(kLongTime)); }
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700105
106 // Returns true if all threads have finished.
107 bool AfterEnd() {
108 if (AtomicOps::Decrement(&threads_active_) == 0) {
109 done_event_.Set();
110 return true;
111 }
112 return false;
113 }
114
115 int threads_active_;
116 Event start_event_;
117 Event done_event_;
118 int shared_value_;
119};
120
danilchap3c6abd22017-09-06 05:46:29 -0700121class RTC_LOCKABLE CriticalSectionLock {
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700122 public:
danilchap3c6abd22017-09-06 05:46:29 -0700123 void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { cs_.Enter(); }
124 void Unlock() RTC_UNLOCK_FUNCTION() { cs_.Leave(); }
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700125
126 private:
127 CriticalSection cs_;
128};
129
130template <class Lock>
131class LockRunner : public RunnerBase {
132 public:
133 LockRunner() : RunnerBase(0) {}
134
135 void OnMessage(Message* msg) override {
136 BeforeStart();
137
138 lock_.Lock();
139
140 EXPECT_EQ(0, shared_value_);
141 int old = shared_value_;
142
143 // Use a loop to increase the chance of race.
144 for (int i = 0; i < kOperationsToRun; ++i) {
145 ++shared_value_;
146 }
147 EXPECT_EQ(old + kOperationsToRun, shared_value_);
148 shared_value_ = 0;
149
150 lock_.Unlock();
151
152 AfterEnd();
153 }
154
155 private:
156 Lock lock_;
157};
158
159template <class Op, class Verifier>
160class AtomicOpRunner : public RunnerBase {
161 public:
162 explicit AtomicOpRunner(int initial_value) : RunnerBase(initial_value) {}
163
164 void OnMessage(Message* msg) override {
165 BeforeStart();
166
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000167 std::vector<int> values;
168 values.reserve(kOperationsToRun);
169
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700170 // Generate a bunch of values by updating shared_value_ atomically.
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000171 for (int i = 0; i < kOperationsToRun; ++i) {
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700172 values.push_back(Op::AtomicOp(&shared_value_));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000173 }
174
Yves Gerey665174f2018-06-19 15:03:05 +0200175 { // Add them all to the set.
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000176 CritScope cs(&all_values_crit_);
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700177 verifier_.Verify(values);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000178 }
179
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700180 if (AfterEnd()) {
181 verifier_.Finalize();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000182 }
183 }
184
185 private:
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000186 CriticalSection all_values_crit_;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700187 Verifier verifier_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000188};
189
190struct IncrementOp {
191 static int AtomicOp(int* i) { return AtomicOps::Increment(i); }
192};
193
194struct DecrementOp {
195 static int AtomicOp(int* i) { return AtomicOps::Decrement(i); }
196};
197
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700198struct CompareAndSwapOp {
199 static int AtomicOp(int* i) { return AtomicOps::CompareAndSwap(i, 0, 1); }
200};
201
nisseb9c2f7c2017-04-20 02:23:08 -0700202void StartThreads(std::vector<std::unique_ptr<Thread>>* threads,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000203 MessageHandler* handler) {
204 for (int i = 0; i < kNumThreads; ++i) {
tommie7251592017-07-14 14:44:46 -0700205 std::unique_ptr<Thread> thread(Thread::Create());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000206 thread->Start();
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700207 thread->Post(RTC_FROM_HERE, handler);
nisseb9c2f7c2017-04-20 02:23:08 -0700208 threads->push_back(std::move(thread));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000209 }
210}
211
212} // namespace
213
214TEST(AtomicOpsTest, Simple) {
215 int value = 0;
216 EXPECT_EQ(1, AtomicOps::Increment(&value));
217 EXPECT_EQ(1, value);
218 EXPECT_EQ(2, AtomicOps::Increment(&value));
219 EXPECT_EQ(2, value);
220 EXPECT_EQ(1, AtomicOps::Decrement(&value));
221 EXPECT_EQ(1, value);
222 EXPECT_EQ(0, AtomicOps::Decrement(&value));
223 EXPECT_EQ(0, value);
224}
225
Peter Boström455a2522015-12-18 17:00:25 +0100226TEST(AtomicOpsTest, SimplePtr) {
227 class Foo {};
228 Foo* volatile foo = nullptr;
jbauch555604a2016-04-26 03:13:22 -0700229 std::unique_ptr<Foo> a(new Foo());
230 std::unique_ptr<Foo> b(new Foo());
Peter Boström455a2522015-12-18 17:00:25 +0100231 // Reading the initial value should work as expected.
232 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == nullptr);
233 // Setting using compare and swap should work.
234 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
235 &foo, static_cast<Foo*>(nullptr), a.get()) == nullptr);
236 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
237 // Setting another value but with the wrong previous pointer should fail
238 // (remain a).
239 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
240 &foo, static_cast<Foo*>(nullptr), b.get()) == a.get());
241 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
242 // Replacing a with b should work.
243 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(&foo, a.get(), b.get()) ==
244 a.get());
245 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == b.get());
246}
247
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000248TEST(AtomicOpsTest, Increment) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000249 // Create and start lots of threads.
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700250 AtomicOpRunner<IncrementOp, UniqueValueVerifier> runner(0);
nisseb9c2f7c2017-04-20 02:23:08 -0700251 std::vector<std::unique_ptr<Thread>> threads;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000252 StartThreads(&threads, &runner);
253 runner.SetExpectedThreadCount(kNumThreads);
254
255 // Release the hounds!
256 EXPECT_TRUE(runner.Run());
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700257 EXPECT_EQ(kOperationsToRun * kNumThreads, runner.shared_value());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000258}
259
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000260TEST(AtomicOpsTest, Decrement) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000261 // Create and start lots of threads.
Yves Gerey665174f2018-06-19 15:03:05 +0200262 AtomicOpRunner<DecrementOp, UniqueValueVerifier> runner(kOperationsToRun *
263 kNumThreads);
nisseb9c2f7c2017-04-20 02:23:08 -0700264 std::vector<std::unique_ptr<Thread>> threads;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000265 StartThreads(&threads, &runner);
266 runner.SetExpectedThreadCount(kNumThreads);
267
268 // Release the hounds!
269 EXPECT_TRUE(runner.Run());
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700270 EXPECT_EQ(0, runner.shared_value());
271}
272
273TEST(AtomicOpsTest, CompareAndSwap) {
274 // Create and start lots of threads.
275 AtomicOpRunner<CompareAndSwapOp, CompareAndSwapVerifier> runner(0);
nisseb9c2f7c2017-04-20 02:23:08 -0700276 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700277 StartThreads(&threads, &runner);
278 runner.SetExpectedThreadCount(kNumThreads);
279
280 // Release the hounds!
281 EXPECT_TRUE(runner.Run());
282 EXPECT_EQ(1, runner.shared_value());
283}
284
Danil Chapovalov5740f3e2019-10-10 11:12:15 +0200285TEST(GlobalLockTest, CanHaveStaticStorageDuration) {
286 static_assert(std::is_trivially_destructible<GlobalLock>::value, "");
287 ABSL_CONST_INIT static GlobalLock global_lock;
288 global_lock.Lock();
289 global_lock.Unlock();
290}
291
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700292TEST(GlobalLockTest, Basic) {
293 // Create and start lots of threads.
294 LockRunner<GlobalLock> runner;
nisseb9c2f7c2017-04-20 02:23:08 -0700295 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700296 StartThreads(&threads, &runner);
297 runner.SetExpectedThreadCount(kNumThreads);
298
299 // Release the hounds!
300 EXPECT_TRUE(runner.Run());
301 EXPECT_EQ(0, runner.shared_value());
302}
303
304TEST(CriticalSectionTest, Basic) {
305 // Create and start lots of threads.
306 LockRunner<CriticalSectionLock> runner;
nisseb9c2f7c2017-04-20 02:23:08 -0700307 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700308 StartThreads(&threads, &runner);
309 runner.SetExpectedThreadCount(kNumThreads);
310
311 // Release the hounds!
312 EXPECT_TRUE(runner.Run());
313 EXPECT_EQ(0, runner.shared_value());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000314}
315
tommied281e92016-01-21 23:47:25 -0800316class PerfTestData {
317 public:
318 PerfTestData(int expected_count, Event* event)
Yves Gerey665174f2018-06-19 15:03:05 +0200319 : cache_line_barrier_1_(),
320 cache_line_barrier_2_(),
321 expected_count_(expected_count),
322 event_(event) {
tommied281e92016-01-21 23:47:25 -0800323 cache_line_barrier_1_[0]++; // Avoid 'is not used'.
324 cache_line_barrier_2_[0]++; // Avoid 'is not used'.
325 }
326 ~PerfTestData() {}
327
328 void AddToCounter(int add) {
329 rtc::CritScope cs(&lock_);
330 my_counter_ += add;
331 if (my_counter_ == expected_count_)
332 event_->Set();
333 }
334
335 int64_t total() const {
336 // Assume that only one thread is running now.
337 return my_counter_;
338 }
339
340 private:
341 uint8_t cache_line_barrier_1_[64];
342 CriticalSection lock_;
343 uint8_t cache_line_barrier_2_[64];
344 int64_t my_counter_ = 0;
345 const int expected_count_;
346 Event* const event_;
347};
348
349class PerfTestThread {
350 public:
351 PerfTestThread() : thread_(&ThreadFunc, this, "CsPerf") {}
352
353 void Start(PerfTestData* data, int repeats, int id) {
354 RTC_DCHECK(!thread_.IsRunning());
355 RTC_DCHECK(!data_);
356 data_ = data;
357 repeats_ = repeats;
358 my_id_ = id;
359 thread_.Start();
360 }
361
362 void Stop() {
363 RTC_DCHECK(thread_.IsRunning());
364 RTC_DCHECK(data_);
365 thread_.Stop();
366 repeats_ = 0;
367 data_ = nullptr;
368 my_id_ = 0;
369 }
370
371 private:
Niels Möller4731f002019-05-03 09:34:24 +0200372 static void ThreadFunc(void* param) {
tommied281e92016-01-21 23:47:25 -0800373 PerfTestThread* me = static_cast<PerfTestThread*>(param);
374 for (int i = 0; i < me->repeats_; ++i)
375 me->data_->AddToCounter(me->my_id_);
tommied281e92016-01-21 23:47:25 -0800376 }
377
378 PlatformThread thread_;
379 PerfTestData* data_ = nullptr;
380 int repeats_ = 0;
381 int my_id_ = 0;
382};
383
Oskar Sundbom13471a42019-03-01 16:11:49 +0100384// Comparison of output of this test as tested on a MacBook Pro, 13-inch,
385// 2017, 3,5 GHz Intel Core i7, 16 GB 2133 MHz LPDDR3,
386// running macOS Mojave, 10.14.3.
tommied281e92016-01-21 23:47:25 -0800387//
Oskar Sundbom13471a42019-03-01 16:11:49 +0100388// Native mutex implementation using fair policy (previously macOS default):
tommied281e92016-01-21 23:47:25 -0800389// Approximate CPU usage:
Oskar Sundbom13471a42019-03-01 16:11:49 +0100390// real 4m54.612s
391// user 1m20.575s
392// sys 3m48.872s
tommied281e92016-01-21 23:47:25 -0800393// Unit test output:
Oskar Sundbom13471a42019-03-01 16:11:49 +0100394// [ OK ] CriticalSectionTest.Performance (294375 ms)
395//
396// Native mutex implementation using first fit policy (current macOS default):
397// Approximate CPU usage:
398// real 0m11.535s
399// user 0m12.738s
400// sys 0m31.207s
401// Unit test output:
402// [ OK ] CriticalSectionTest.Performance (11444 ms)
tommied281e92016-01-21 23:47:25 -0800403//
404// Special partially spin lock based implementation:
405// Approximate CPU usage:
Oskar Sundbom13471a42019-03-01 16:11:49 +0100406// real 0m2.113s
407// user 0m3.014s
408// sys 0m4.495s
tommied281e92016-01-21 23:47:25 -0800409// Unit test output:
Oskar Sundbom13471a42019-03-01 16:11:49 +0100410// [ OK ] CriticalSectionTest.Performance (1885 ms)
tommied281e92016-01-21 23:47:25 -0800411//
412// The test is disabled by default to avoid unecessarily loading the bots.
413TEST(CriticalSectionTest, DISABLED_Performance) {
414 PerfTestThread threads[8];
Niels Möllerc572ff32018-11-07 08:43:50 +0100415 Event event;
tommied281e92016-01-21 23:47:25 -0800416
417 static const int kThreadRepeats = 10000000;
418 static const int kExpectedCount = kThreadRepeats * arraysize(threads);
419 PerfTestData test_data(kExpectedCount, &event);
420
421 for (auto& t : threads)
422 t.Start(&test_data, kThreadRepeats, 1);
423
424 event.Wait(Event::kForever);
425
426 for (auto& t : threads)
427 t.Stop();
428}
429
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000430} // namespace rtc