blob: 16aefd27400212ac54c31c96ebb9924ff17485a1 [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
Jonas Olssona4d87372019-07-05 17:08:3311#include "rtc_base/critical_section.h"
12
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"
Steve Anton10542f22019-01-11 17:11:0024#include "rtc_base/atomic_ops.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3125#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3126#include "rtc_base/event.h"
Yves Gerey3e707812018-11-28 15:47:4927#include "rtc_base/location.h"
Steve Anton10542f22019-01-11 17:11:0028#include "rtc_base/message_handler.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3129#include "rtc_base/platform_thread.h"
30#include "rtc_base/thread.h"
Yves Gerey3e707812018-11-28 15:47:4931#include "test/gtest.h"
henrike@webrtc.orgf0488722014-05-13 18:00:2632
33namespace rtc {
34
35namespace {
36
37const int kLongTime = 10000; // 10 seconds
38const int kNumThreads = 16;
39const int kOperationsToRun = 1000;
40
Jiayang Liubef8d2d2015-03-26 21:38:4641class UniqueValueVerifier {
henrike@webrtc.orgf0488722014-05-13 18:00:2642 public:
Jiayang Liubef8d2d2015-03-26 21:38:4643 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:2653
Jiayang Liubef8d2d2015-03-26 21:38:4654 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 13:03:0575 void Finalize() { EXPECT_EQ(1, zero_count_); }
76
Jiayang Liubef8d2d2015-03-26 21:38:4677 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:2688
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 13:03:0597 void SetExpectedThreadCount(int count) { threads_active_ = count; }
henrike@webrtc.orgf0488722014-05-13 18:00:2698
Jiayang Liubef8d2d2015-03-26 21:38:4699 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 13:03:05104 void BeforeStart() { ASSERT_TRUE(start_event_.Wait(kLongTime)); }
Jiayang Liubef8d2d2015-03-26 21:38:46105
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 12:46:29121class RTC_LOCKABLE CriticalSectionLock {
Jiayang Liubef8d2d2015-03-26 21:38:46122 public:
danilchap3c6abd22017-09-06 12:46:29123 void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { cs_.Enter(); }
124 void Unlock() RTC_UNLOCK_FUNCTION() { cs_.Leave(); }
Jiayang Liubef8d2d2015-03-26 21:38:46125
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:26167 std::vector<int> values;
168 values.reserve(kOperationsToRun);
169
Jiayang Liubef8d2d2015-03-26 21:38:46170 // Generate a bunch of values by updating shared_value_ atomically.
henrike@webrtc.orgf0488722014-05-13 18:00:26171 for (int i = 0; i < kOperationsToRun; ++i) {
Jiayang Liubef8d2d2015-03-26 21:38:46172 values.push_back(Op::AtomicOp(&shared_value_));
henrike@webrtc.orgf0488722014-05-13 18:00:26173 }
174
Yves Gerey665174f2018-06-19 13:03:05175 { // Add them all to the set.
henrike@webrtc.orgf0488722014-05-13 18:00:26176 CritScope cs(&all_values_crit_);
Jiayang Liubef8d2d2015-03-26 21:38:46177 verifier_.Verify(values);
henrike@webrtc.orgf0488722014-05-13 18:00:26178 }
179
Jiayang Liubef8d2d2015-03-26 21:38:46180 if (AfterEnd()) {
181 verifier_.Finalize();
henrike@webrtc.orgf0488722014-05-13 18:00:26182 }
183 }
184
185 private:
henrike@webrtc.orgf0488722014-05-13 18:00:26186 CriticalSection all_values_crit_;
Jiayang Liubef8d2d2015-03-26 21:38:46187 Verifier verifier_;
henrike@webrtc.orgf0488722014-05-13 18:00:26188};
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 21:38:46198struct CompareAndSwapOp {
199 static int AtomicOp(int* i) { return AtomicOps::CompareAndSwap(i, 0, 1); }
200};
201
nisseb9c2f7c2017-04-20 09:23:08202void StartThreads(std::vector<std::unique_ptr<Thread>>* threads,
henrike@webrtc.orgf0488722014-05-13 18:00:26203 MessageHandler* handler) {
204 for (int i = 0; i < kNumThreads; ++i) {
tommie7251592017-07-14 21:44:46205 std::unique_ptr<Thread> thread(Thread::Create());
henrike@webrtc.orgf0488722014-05-13 18:00:26206 thread->Start();
Taylor Brandstetter5d97a9a2016-06-10 21:17:27207 thread->Post(RTC_FROM_HERE, handler);
nisseb9c2f7c2017-04-20 09:23:08208 threads->push_back(std::move(thread));
henrike@webrtc.orgf0488722014-05-13 18:00:26209 }
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 16:00:25226TEST(AtomicOpsTest, SimplePtr) {
227 class Foo {};
228 Foo* volatile foo = nullptr;
jbauch555604a2016-04-26 10:13:22229 std::unique_ptr<Foo> a(new Foo());
230 std::unique_ptr<Foo> b(new Foo());
Peter Boström455a2522015-12-18 16:00:25231 // 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:15248TEST(AtomicOpsTest, Increment) {
henrike@webrtc.orgf0488722014-05-13 18:00:26249 // Create and start lots of threads.
Jiayang Liubef8d2d2015-03-26 21:38:46250 AtomicOpRunner<IncrementOp, UniqueValueVerifier> runner(0);
nisseb9c2f7c2017-04-20 09:23:08251 std::vector<std::unique_ptr<Thread>> threads;
henrike@webrtc.orgf0488722014-05-13 18:00:26252 StartThreads(&threads, &runner);
253 runner.SetExpectedThreadCount(kNumThreads);
254
255 // Release the hounds!
256 EXPECT_TRUE(runner.Run());
Jiayang Liubef8d2d2015-03-26 21:38:46257 EXPECT_EQ(kOperationsToRun * kNumThreads, runner.shared_value());
henrike@webrtc.orgf0488722014-05-13 18:00:26258}
259
henrike@webrtc.orgc732a3e2014-10-09 22:08:15260TEST(AtomicOpsTest, Decrement) {
henrike@webrtc.orgf0488722014-05-13 18:00:26261 // Create and start lots of threads.
Yves Gerey665174f2018-06-19 13:03:05262 AtomicOpRunner<DecrementOp, UniqueValueVerifier> runner(kOperationsToRun *
263 kNumThreads);
nisseb9c2f7c2017-04-20 09:23:08264 std::vector<std::unique_ptr<Thread>> threads;
henrike@webrtc.orgf0488722014-05-13 18:00:26265 StartThreads(&threads, &runner);
266 runner.SetExpectedThreadCount(kNumThreads);
267
268 // Release the hounds!
269 EXPECT_TRUE(runner.Run());
Jiayang Liubef8d2d2015-03-26 21:38:46270 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 09:23:08276 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 21:38:46277 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 09:12:15285TEST(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 21:38:46292TEST(GlobalLockTest, Basic) {
293 // Create and start lots of threads.
294 LockRunner<GlobalLock> runner;
nisseb9c2f7c2017-04-20 09:23:08295 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 21:38:46296 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 09:23:08307 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 21:38:46308 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:26314}
315
tommied281e92016-01-22 07:47:25316class PerfTestData {
317 public:
318 PerfTestData(int expected_count, Event* event)
Yves Gerey665174f2018-06-19 13:03:05319 : cache_line_barrier_1_(),
320 cache_line_barrier_2_(),
321 expected_count_(expected_count),
322 event_(event) {
tommied281e92016-01-22 07:47:25323 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 07:34:24372 static void ThreadFunc(void* param) {
tommied281e92016-01-22 07:47:25373 PerfTestThread* me = static_cast<PerfTestThread*>(param);
374 for (int i = 0; i < me->repeats_; ++i)
375 me->data_->AddToCounter(me->my_id_);
tommied281e92016-01-22 07:47:25376 }
377
378 PlatformThread thread_;
379 PerfTestData* data_ = nullptr;
380 int repeats_ = 0;
381 int my_id_ = 0;
382};
383
Oskar Sundbom13471a42019-03-01 15:11:49384// 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-22 07:47:25387//
Oskar Sundbom13471a42019-03-01 15:11:49388// Native mutex implementation using fair policy (previously macOS default):
tommied281e92016-01-22 07:47:25389// Approximate CPU usage:
Oskar Sundbom13471a42019-03-01 15:11:49390// real 4m54.612s
391// user 1m20.575s
392// sys 3m48.872s
tommied281e92016-01-22 07:47:25393// Unit test output:
Oskar Sundbom13471a42019-03-01 15:11:49394// [ 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-22 07:47:25403//
404// Special partially spin lock based implementation:
405// Approximate CPU usage:
Oskar Sundbom13471a42019-03-01 15:11:49406// real 0m2.113s
407// user 0m3.014s
408// sys 0m4.495s
tommied281e92016-01-22 07:47:25409// Unit test output:
Oskar Sundbom13471a42019-03-01 15:11:49410// [ OK ] CriticalSectionTest.Performance (1885 ms)
tommied281e92016-01-22 07:47:25411//
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 07:43:50415 Event event;
tommied281e92016-01-22 07:47:25416
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:26430} // namespace rtc