blob: e384e9874d66b332b6e2ddfee6dc83eea2881818 [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"
29#include "rtc_base/message_queue.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020030#include "rtc_base/platform_thread.h"
31#include "rtc_base/thread.h"
Yves Gerey3e707812018-11-28 16:47:49 +010032#include "test/gtest.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000033
34namespace rtc {
35
36namespace {
37
38const int kLongTime = 10000; // 10 seconds
39const int kNumThreads = 16;
40const int kOperationsToRun = 1000;
41
Jiayang Liubef8d2d2015-03-26 14:38:46 -070042class UniqueValueVerifier {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000043 public:
Jiayang Liubef8d2d2015-03-26 14:38:46 -070044 void Verify(const std::vector<int>& values) {
45 for (size_t i = 0; i < values.size(); ++i) {
46 std::pair<std::set<int>::iterator, bool> result =
47 all_values_.insert(values[i]);
48 // Each value should only be taken by one thread, so if this value
49 // has already been added, something went wrong.
50 EXPECT_TRUE(result.second)
51 << " Thread=" << Thread::Current() << " value=" << values[i];
52 }
53 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000054
Jiayang Liubef8d2d2015-03-26 14:38:46 -070055 void Finalize() {}
56
57 private:
58 std::set<int> all_values_;
59};
60
61class CompareAndSwapVerifier {
62 public:
63 CompareAndSwapVerifier() : zero_count_(0) {}
64
65 void Verify(const std::vector<int>& values) {
66 for (auto v : values) {
67 if (v == 0) {
68 EXPECT_EQ(0, zero_count_) << "Thread=" << Thread::Current();
69 ++zero_count_;
70 } else {
71 EXPECT_EQ(1, v) << " Thread=" << Thread::Current();
72 }
73 }
74 }
75
Yves Gerey665174f2018-06-19 15:03:05 +020076 void Finalize() { EXPECT_EQ(1, zero_count_); }
77
Jiayang Liubef8d2d2015-03-26 14:38:46 -070078 private:
79 int zero_count_;
80};
81
82class RunnerBase : public MessageHandler {
83 public:
84 explicit RunnerBase(int value)
85 : threads_active_(0),
86 start_event_(true, false),
87 done_event_(true, false),
88 shared_value_(value) {}
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000089
90 bool Run() {
91 // Signal all threads to start.
92 start_event_.Set();
93
94 // Wait for all threads to finish.
95 return done_event_.Wait(kLongTime);
96 }
97
Yves Gerey665174f2018-06-19 15:03:05 +020098 void SetExpectedThreadCount(int count) { threads_active_ = count; }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000099
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700100 int shared_value() const { return shared_value_; }
101
102 protected:
103 // Derived classes must override OnMessage, and call BeforeStart and AfterEnd
104 // at the beginning and the end of OnMessage respectively.
Yves Gerey665174f2018-06-19 15:03:05 +0200105 void BeforeStart() { ASSERT_TRUE(start_event_.Wait(kLongTime)); }
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700106
107 // Returns true if all threads have finished.
108 bool AfterEnd() {
109 if (AtomicOps::Decrement(&threads_active_) == 0) {
110 done_event_.Set();
111 return true;
112 }
113 return false;
114 }
115
116 int threads_active_;
117 Event start_event_;
118 Event done_event_;
119 int shared_value_;
120};
121
danilchap3c6abd22017-09-06 05:46:29 -0700122class RTC_LOCKABLE CriticalSectionLock {
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700123 public:
danilchap3c6abd22017-09-06 05:46:29 -0700124 void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { cs_.Enter(); }
125 void Unlock() RTC_UNLOCK_FUNCTION() { cs_.Leave(); }
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700126
127 private:
128 CriticalSection cs_;
129};
130
131template <class Lock>
132class LockRunner : public RunnerBase {
133 public:
134 LockRunner() : RunnerBase(0) {}
135
136 void OnMessage(Message* msg) override {
137 BeforeStart();
138
139 lock_.Lock();
140
141 EXPECT_EQ(0, shared_value_);
142 int old = shared_value_;
143
144 // Use a loop to increase the chance of race.
145 for (int i = 0; i < kOperationsToRun; ++i) {
146 ++shared_value_;
147 }
148 EXPECT_EQ(old + kOperationsToRun, shared_value_);
149 shared_value_ = 0;
150
151 lock_.Unlock();
152
153 AfterEnd();
154 }
155
156 private:
157 Lock lock_;
158};
159
160template <class Op, class Verifier>
161class AtomicOpRunner : public RunnerBase {
162 public:
163 explicit AtomicOpRunner(int initial_value) : RunnerBase(initial_value) {}
164
165 void OnMessage(Message* msg) override {
166 BeforeStart();
167
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000168 std::vector<int> values;
169 values.reserve(kOperationsToRun);
170
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700171 // Generate a bunch of values by updating shared_value_ atomically.
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000172 for (int i = 0; i < kOperationsToRun; ++i) {
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700173 values.push_back(Op::AtomicOp(&shared_value_));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000174 }
175
Yves Gerey665174f2018-06-19 15:03:05 +0200176 { // Add them all to the set.
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000177 CritScope cs(&all_values_crit_);
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700178 verifier_.Verify(values);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000179 }
180
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700181 if (AfterEnd()) {
182 verifier_.Finalize();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000183 }
184 }
185
186 private:
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000187 CriticalSection all_values_crit_;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700188 Verifier verifier_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000189};
190
191struct IncrementOp {
192 static int AtomicOp(int* i) { return AtomicOps::Increment(i); }
193};
194
195struct DecrementOp {
196 static int AtomicOp(int* i) { return AtomicOps::Decrement(i); }
197};
198
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700199struct CompareAndSwapOp {
200 static int AtomicOp(int* i) { return AtomicOps::CompareAndSwap(i, 0, 1); }
201};
202
nisseb9c2f7c2017-04-20 02:23:08 -0700203void StartThreads(std::vector<std::unique_ptr<Thread>>* threads,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000204 MessageHandler* handler) {
205 for (int i = 0; i < kNumThreads; ++i) {
tommie7251592017-07-14 14:44:46 -0700206 std::unique_ptr<Thread> thread(Thread::Create());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000207 thread->Start();
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700208 thread->Post(RTC_FROM_HERE, handler);
nisseb9c2f7c2017-04-20 02:23:08 -0700209 threads->push_back(std::move(thread));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000210 }
211}
212
213} // namespace
214
215TEST(AtomicOpsTest, Simple) {
216 int value = 0;
217 EXPECT_EQ(1, AtomicOps::Increment(&value));
218 EXPECT_EQ(1, value);
219 EXPECT_EQ(2, AtomicOps::Increment(&value));
220 EXPECT_EQ(2, value);
221 EXPECT_EQ(1, AtomicOps::Decrement(&value));
222 EXPECT_EQ(1, value);
223 EXPECT_EQ(0, AtomicOps::Decrement(&value));
224 EXPECT_EQ(0, value);
225}
226
Peter Boström455a2522015-12-18 17:00:25 +0100227TEST(AtomicOpsTest, SimplePtr) {
228 class Foo {};
229 Foo* volatile foo = nullptr;
jbauch555604a2016-04-26 03:13:22 -0700230 std::unique_ptr<Foo> a(new Foo());
231 std::unique_ptr<Foo> b(new Foo());
Peter Boström455a2522015-12-18 17:00:25 +0100232 // Reading the initial value should work as expected.
233 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == nullptr);
234 // Setting using compare and swap should work.
235 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
236 &foo, static_cast<Foo*>(nullptr), a.get()) == nullptr);
237 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
238 // Setting another value but with the wrong previous pointer should fail
239 // (remain a).
240 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
241 &foo, static_cast<Foo*>(nullptr), b.get()) == a.get());
242 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
243 // Replacing a with b should work.
244 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(&foo, a.get(), b.get()) ==
245 a.get());
246 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == b.get());
247}
248
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000249TEST(AtomicOpsTest, Increment) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000250 // Create and start lots of threads.
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700251 AtomicOpRunner<IncrementOp, UniqueValueVerifier> runner(0);
nisseb9c2f7c2017-04-20 02:23:08 -0700252 std::vector<std::unique_ptr<Thread>> threads;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000253 StartThreads(&threads, &runner);
254 runner.SetExpectedThreadCount(kNumThreads);
255
256 // Release the hounds!
257 EXPECT_TRUE(runner.Run());
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700258 EXPECT_EQ(kOperationsToRun * kNumThreads, runner.shared_value());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000259}
260
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000261TEST(AtomicOpsTest, Decrement) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000262 // Create and start lots of threads.
Yves Gerey665174f2018-06-19 15:03:05 +0200263 AtomicOpRunner<DecrementOp, UniqueValueVerifier> runner(kOperationsToRun *
264 kNumThreads);
nisseb9c2f7c2017-04-20 02:23:08 -0700265 std::vector<std::unique_ptr<Thread>> threads;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000266 StartThreads(&threads, &runner);
267 runner.SetExpectedThreadCount(kNumThreads);
268
269 // Release the hounds!
270 EXPECT_TRUE(runner.Run());
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700271 EXPECT_EQ(0, runner.shared_value());
272}
273
274TEST(AtomicOpsTest, CompareAndSwap) {
275 // Create and start lots of threads.
276 AtomicOpRunner<CompareAndSwapOp, CompareAndSwapVerifier> runner(0);
nisseb9c2f7c2017-04-20 02:23:08 -0700277 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700278 StartThreads(&threads, &runner);
279 runner.SetExpectedThreadCount(kNumThreads);
280
281 // Release the hounds!
282 EXPECT_TRUE(runner.Run());
283 EXPECT_EQ(1, runner.shared_value());
284}
285
Danil Chapovalov5740f3e2019-10-10 11:12:15 +0200286TEST(GlobalLockTest, CanHaveStaticStorageDuration) {
287 static_assert(std::is_trivially_destructible<GlobalLock>::value, "");
288 ABSL_CONST_INIT static GlobalLock global_lock;
289 global_lock.Lock();
290 global_lock.Unlock();
291}
292
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700293TEST(GlobalLockTest, Basic) {
294 // Create and start lots of threads.
295 LockRunner<GlobalLock> runner;
nisseb9c2f7c2017-04-20 02:23:08 -0700296 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700297 StartThreads(&threads, &runner);
298 runner.SetExpectedThreadCount(kNumThreads);
299
300 // Release the hounds!
301 EXPECT_TRUE(runner.Run());
302 EXPECT_EQ(0, runner.shared_value());
303}
304
305TEST(CriticalSectionTest, Basic) {
306 // Create and start lots of threads.
307 LockRunner<CriticalSectionLock> runner;
nisseb9c2f7c2017-04-20 02:23:08 -0700308 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700309 StartThreads(&threads, &runner);
310 runner.SetExpectedThreadCount(kNumThreads);
311
312 // Release the hounds!
313 EXPECT_TRUE(runner.Run());
314 EXPECT_EQ(0, runner.shared_value());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000315}
316
tommied281e92016-01-21 23:47:25 -0800317class PerfTestData {
318 public:
319 PerfTestData(int expected_count, Event* event)
Yves Gerey665174f2018-06-19 15:03:05 +0200320 : cache_line_barrier_1_(),
321 cache_line_barrier_2_(),
322 expected_count_(expected_count),
323 event_(event) {
tommied281e92016-01-21 23:47:25 -0800324 cache_line_barrier_1_[0]++; // Avoid 'is not used'.
325 cache_line_barrier_2_[0]++; // Avoid 'is not used'.
326 }
327 ~PerfTestData() {}
328
329 void AddToCounter(int add) {
330 rtc::CritScope cs(&lock_);
331 my_counter_ += add;
332 if (my_counter_ == expected_count_)
333 event_->Set();
334 }
335
336 int64_t total() const {
337 // Assume that only one thread is running now.
338 return my_counter_;
339 }
340
341 private:
342 uint8_t cache_line_barrier_1_[64];
343 CriticalSection lock_;
344 uint8_t cache_line_barrier_2_[64];
345 int64_t my_counter_ = 0;
346 const int expected_count_;
347 Event* const event_;
348};
349
350class PerfTestThread {
351 public:
352 PerfTestThread() : thread_(&ThreadFunc, this, "CsPerf") {}
353
354 void Start(PerfTestData* data, int repeats, int id) {
355 RTC_DCHECK(!thread_.IsRunning());
356 RTC_DCHECK(!data_);
357 data_ = data;
358 repeats_ = repeats;
359 my_id_ = id;
360 thread_.Start();
361 }
362
363 void Stop() {
364 RTC_DCHECK(thread_.IsRunning());
365 RTC_DCHECK(data_);
366 thread_.Stop();
367 repeats_ = 0;
368 data_ = nullptr;
369 my_id_ = 0;
370 }
371
372 private:
Niels Möller4731f002019-05-03 09:34:24 +0200373 static void ThreadFunc(void* param) {
tommied281e92016-01-21 23:47:25 -0800374 PerfTestThread* me = static_cast<PerfTestThread*>(param);
375 for (int i = 0; i < me->repeats_; ++i)
376 me->data_->AddToCounter(me->my_id_);
tommied281e92016-01-21 23:47:25 -0800377 }
378
379 PlatformThread thread_;
380 PerfTestData* data_ = nullptr;
381 int repeats_ = 0;
382 int my_id_ = 0;
383};
384
Oskar Sundbom13471a42019-03-01 16:11:49 +0100385// Comparison of output of this test as tested on a MacBook Pro, 13-inch,
386// 2017, 3,5 GHz Intel Core i7, 16 GB 2133 MHz LPDDR3,
387// running macOS Mojave, 10.14.3.
tommied281e92016-01-21 23:47:25 -0800388//
Oskar Sundbom13471a42019-03-01 16:11:49 +0100389// Native mutex implementation using fair policy (previously macOS default):
tommied281e92016-01-21 23:47:25 -0800390// Approximate CPU usage:
Oskar Sundbom13471a42019-03-01 16:11:49 +0100391// real 4m54.612s
392// user 1m20.575s
393// sys 3m48.872s
tommied281e92016-01-21 23:47:25 -0800394// Unit test output:
Oskar Sundbom13471a42019-03-01 16:11:49 +0100395// [ OK ] CriticalSectionTest.Performance (294375 ms)
396//
397// Native mutex implementation using first fit policy (current macOS default):
398// Approximate CPU usage:
399// real 0m11.535s
400// user 0m12.738s
401// sys 0m31.207s
402// Unit test output:
403// [ OK ] CriticalSectionTest.Performance (11444 ms)
tommied281e92016-01-21 23:47:25 -0800404//
405// Special partially spin lock based implementation:
406// Approximate CPU usage:
Oskar Sundbom13471a42019-03-01 16:11:49 +0100407// real 0m2.113s
408// user 0m3.014s
409// sys 0m4.495s
tommied281e92016-01-21 23:47:25 -0800410// Unit test output:
Oskar Sundbom13471a42019-03-01 16:11:49 +0100411// [ OK ] CriticalSectionTest.Performance (1885 ms)
tommied281e92016-01-21 23:47:25 -0800412//
413// The test is disabled by default to avoid unecessarily loading the bots.
414TEST(CriticalSectionTest, DISABLED_Performance) {
415 PerfTestThread threads[8];
Niels Möllerc572ff32018-11-07 08:43:50 +0100416 Event event;
tommied281e92016-01-21 23:47:25 -0800417
418 static const int kThreadRepeats = 10000000;
419 static const int kExpectedCount = kThreadRepeats * arraysize(threads);
420 PerfTestData test_data(kExpectedCount, &event);
421
422 for (auto& t : threads)
423 t.Start(&test_data, kThreadRepeats, 1);
424
425 event.Wait(Event::kForever);
426
427 for (auto& t : threads)
428 t.Stop();
429}
430
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000431} // namespace rtc