blob: f5d6957fcf661b61a2e906dc5c139aa7b4d258a8 [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
jbauch555604a2016-04-26 03:13:22 -070011#include <memory>
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000012#include <set>
13#include <vector>
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "rtc_base/arraysize.h"
16#include "rtc_base/checks.h"
17#include "rtc_base/criticalsection.h"
18#include "rtc_base/event.h"
19#include "rtc_base/gunit.h"
20#include "rtc_base/platform_thread.h"
21#include "rtc_base/thread.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000022
23namespace rtc {
24
25namespace {
26
27const int kLongTime = 10000; // 10 seconds
28const int kNumThreads = 16;
29const int kOperationsToRun = 1000;
30
Jiayang Liubef8d2d2015-03-26 14:38:46 -070031class UniqueValueVerifier {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000032 public:
Jiayang Liubef8d2d2015-03-26 14:38:46 -070033 void Verify(const std::vector<int>& values) {
34 for (size_t i = 0; i < values.size(); ++i) {
35 std::pair<std::set<int>::iterator, bool> result =
36 all_values_.insert(values[i]);
37 // Each value should only be taken by one thread, so if this value
38 // has already been added, something went wrong.
39 EXPECT_TRUE(result.second)
40 << " Thread=" << Thread::Current() << " value=" << values[i];
41 }
42 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000043
Jiayang Liubef8d2d2015-03-26 14:38:46 -070044 void Finalize() {}
45
46 private:
47 std::set<int> all_values_;
48};
49
50class CompareAndSwapVerifier {
51 public:
52 CompareAndSwapVerifier() : zero_count_(0) {}
53
54 void Verify(const std::vector<int>& values) {
55 for (auto v : values) {
56 if (v == 0) {
57 EXPECT_EQ(0, zero_count_) << "Thread=" << Thread::Current();
58 ++zero_count_;
59 } else {
60 EXPECT_EQ(1, v) << " Thread=" << Thread::Current();
61 }
62 }
63 }
64
65 void Finalize() {
66 EXPECT_EQ(1, zero_count_);
67 }
68 private:
69 int zero_count_;
70};
71
72class RunnerBase : public MessageHandler {
73 public:
74 explicit RunnerBase(int value)
75 : threads_active_(0),
76 start_event_(true, false),
77 done_event_(true, false),
78 shared_value_(value) {}
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000079
80 bool Run() {
81 // Signal all threads to start.
82 start_event_.Set();
83
84 // Wait for all threads to finish.
85 return done_event_.Wait(kLongTime);
86 }
87
88 void SetExpectedThreadCount(int count) {
89 threads_active_ = count;
90 }
91
Jiayang Liubef8d2d2015-03-26 14:38:46 -070092 int shared_value() const { return shared_value_; }
93
94 protected:
95 // Derived classes must override OnMessage, and call BeforeStart and AfterEnd
96 // at the beginning and the end of OnMessage respectively.
97 void BeforeStart() {
98 ASSERT_TRUE(start_event_.Wait(kLongTime));
99 }
100
101 // Returns true if all threads have finished.
102 bool AfterEnd() {
103 if (AtomicOps::Decrement(&threads_active_) == 0) {
104 done_event_.Set();
105 return true;
106 }
107 return false;
108 }
109
110 int threads_active_;
111 Event start_event_;
112 Event done_event_;
113 int shared_value_;
114};
115
danilchap3c6abd22017-09-06 05:46:29 -0700116class RTC_LOCKABLE CriticalSectionLock {
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700117 public:
danilchap3c6abd22017-09-06 05:46:29 -0700118 void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { cs_.Enter(); }
119 void Unlock() RTC_UNLOCK_FUNCTION() { cs_.Leave(); }
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700120
121 private:
122 CriticalSection cs_;
123};
124
125template <class Lock>
126class LockRunner : public RunnerBase {
127 public:
128 LockRunner() : RunnerBase(0) {}
129
130 void OnMessage(Message* msg) override {
131 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
154template <class Op, class Verifier>
155class AtomicOpRunner : public RunnerBase {
156 public:
157 explicit AtomicOpRunner(int initial_value) : RunnerBase(initial_value) {}
158
159 void OnMessage(Message* msg) override {
160 BeforeStart();
161
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000162 std::vector<int> values;
163 values.reserve(kOperationsToRun);
164
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700165 // Generate a bunch of values by updating shared_value_ atomically.
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000166 for (int i = 0; i < kOperationsToRun; ++i) {
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700167 values.push_back(Op::AtomicOp(&shared_value_));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000168 }
169
170 { // Add them all to the set.
171 CritScope cs(&all_values_crit_);
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700172 verifier_.Verify(values);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000173 }
174
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700175 if (AfterEnd()) {
176 verifier_.Finalize();
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000177 }
178 }
179
180 private:
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000181 CriticalSection all_values_crit_;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700182 Verifier verifier_;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000183};
184
185struct IncrementOp {
186 static int AtomicOp(int* i) { return AtomicOps::Increment(i); }
187};
188
189struct DecrementOp {
190 static int AtomicOp(int* i) { return AtomicOps::Decrement(i); }
191};
192
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700193struct CompareAndSwapOp {
194 static int AtomicOp(int* i) { return AtomicOps::CompareAndSwap(i, 0, 1); }
195};
196
nisseb9c2f7c2017-04-20 02:23:08 -0700197void StartThreads(std::vector<std::unique_ptr<Thread>>* threads,
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000198 MessageHandler* handler) {
199 for (int i = 0; i < kNumThreads; ++i) {
tommie7251592017-07-14 14:44:46 -0700200 std::unique_ptr<Thread> thread(Thread::Create());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000201 thread->Start();
Taylor Brandstetter5d97a9a2016-06-10 14:17:27 -0700202 thread->Post(RTC_FROM_HERE, handler);
nisseb9c2f7c2017-04-20 02:23:08 -0700203 threads->push_back(std::move(thread));
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000204 }
205}
206
207} // namespace
208
209TEST(AtomicOpsTest, Simple) {
210 int value = 0;
211 EXPECT_EQ(1, AtomicOps::Increment(&value));
212 EXPECT_EQ(1, value);
213 EXPECT_EQ(2, AtomicOps::Increment(&value));
214 EXPECT_EQ(2, value);
215 EXPECT_EQ(1, AtomicOps::Decrement(&value));
216 EXPECT_EQ(1, value);
217 EXPECT_EQ(0, AtomicOps::Decrement(&value));
218 EXPECT_EQ(0, value);
219}
220
Peter Boström455a2522015-12-18 17:00:25 +0100221TEST(AtomicOpsTest, SimplePtr) {
222 class Foo {};
223 Foo* volatile foo = nullptr;
jbauch555604a2016-04-26 03:13:22 -0700224 std::unique_ptr<Foo> a(new Foo());
225 std::unique_ptr<Foo> b(new Foo());
Peter Boström455a2522015-12-18 17:00:25 +0100226 // Reading the initial value should work as expected.
227 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == nullptr);
228 // Setting using compare and swap should work.
229 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
230 &foo, static_cast<Foo*>(nullptr), a.get()) == nullptr);
231 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
232 // Setting another value but with the wrong previous pointer should fail
233 // (remain a).
234 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(
235 &foo, static_cast<Foo*>(nullptr), b.get()) == a.get());
236 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == a.get());
237 // Replacing a with b should work.
238 EXPECT_TRUE(rtc::AtomicOps::CompareAndSwapPtr(&foo, a.get(), b.get()) ==
239 a.get());
240 EXPECT_TRUE(rtc::AtomicOps::AcquireLoadPtr(&foo) == b.get());
241}
242
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000243TEST(AtomicOpsTest, Increment) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000244 // Create and start lots of threads.
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700245 AtomicOpRunner<IncrementOp, UniqueValueVerifier> runner(0);
nisseb9c2f7c2017-04-20 02:23:08 -0700246 std::vector<std::unique_ptr<Thread>> threads;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000247 StartThreads(&threads, &runner);
248 runner.SetExpectedThreadCount(kNumThreads);
249
250 // Release the hounds!
251 EXPECT_TRUE(runner.Run());
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700252 EXPECT_EQ(kOperationsToRun * kNumThreads, runner.shared_value());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000253}
254
henrike@webrtc.orgc732a3e2014-10-09 22:08:15 +0000255TEST(AtomicOpsTest, Decrement) {
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000256 // Create and start lots of threads.
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700257 AtomicOpRunner<DecrementOp, UniqueValueVerifier> runner(
258 kOperationsToRun * kNumThreads);
nisseb9c2f7c2017-04-20 02:23:08 -0700259 std::vector<std::unique_ptr<Thread>> threads;
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000260 StartThreads(&threads, &runner);
261 runner.SetExpectedThreadCount(kNumThreads);
262
263 // Release the hounds!
264 EXPECT_TRUE(runner.Run());
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700265 EXPECT_EQ(0, runner.shared_value());
266}
267
268TEST(AtomicOpsTest, CompareAndSwap) {
269 // Create and start lots of threads.
270 AtomicOpRunner<CompareAndSwapOp, CompareAndSwapVerifier> runner(0);
nisseb9c2f7c2017-04-20 02:23:08 -0700271 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700272 StartThreads(&threads, &runner);
273 runner.SetExpectedThreadCount(kNumThreads);
274
275 // Release the hounds!
276 EXPECT_TRUE(runner.Run());
277 EXPECT_EQ(1, runner.shared_value());
278}
279
280TEST(GlobalLockTest, Basic) {
281 // Create and start lots of threads.
282 LockRunner<GlobalLock> runner;
nisseb9c2f7c2017-04-20 02:23:08 -0700283 std::vector<std::unique_ptr<Thread>> threads;
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700284 StartThreads(&threads, &runner);
285 runner.SetExpectedThreadCount(kNumThreads);
286
287 // Release the hounds!
288 EXPECT_TRUE(runner.Run());
289 EXPECT_EQ(0, runner.shared_value());
290}
291
292TEST(CriticalSectionTest, Basic) {
293 // Create and start lots of threads.
294 LockRunner<CriticalSectionLock> 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());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000302}
303
tommied281e92016-01-21 23:47:25 -0800304class PerfTestData {
305 public:
306 PerfTestData(int expected_count, Event* event)
307 : cache_line_barrier_1_(), cache_line_barrier_2_(),
308 expected_count_(expected_count), event_(event) {
309 cache_line_barrier_1_[0]++; // Avoid 'is not used'.
310 cache_line_barrier_2_[0]++; // Avoid 'is not used'.
311 }
312 ~PerfTestData() {}
313
314 void AddToCounter(int add) {
315 rtc::CritScope cs(&lock_);
316 my_counter_ += add;
317 if (my_counter_ == expected_count_)
318 event_->Set();
319 }
320
321 int64_t total() const {
322 // Assume that only one thread is running now.
323 return my_counter_;
324 }
325
326 private:
327 uint8_t cache_line_barrier_1_[64];
328 CriticalSection lock_;
329 uint8_t cache_line_barrier_2_[64];
330 int64_t my_counter_ = 0;
331 const int expected_count_;
332 Event* const event_;
333};
334
335class PerfTestThread {
336 public:
337 PerfTestThread() : thread_(&ThreadFunc, this, "CsPerf") {}
338
339 void Start(PerfTestData* data, int repeats, int id) {
340 RTC_DCHECK(!thread_.IsRunning());
341 RTC_DCHECK(!data_);
342 data_ = data;
343 repeats_ = repeats;
344 my_id_ = id;
345 thread_.Start();
346 }
347
348 void Stop() {
349 RTC_DCHECK(thread_.IsRunning());
350 RTC_DCHECK(data_);
351 thread_.Stop();
352 repeats_ = 0;
353 data_ = nullptr;
354 my_id_ = 0;
355 }
356
357 private:
358 static bool ThreadFunc(void* param) {
359 PerfTestThread* me = static_cast<PerfTestThread*>(param);
360 for (int i = 0; i < me->repeats_; ++i)
361 me->data_->AddToCounter(me->my_id_);
362 return false;
363 }
364
365 PlatformThread thread_;
366 PerfTestData* data_ = nullptr;
367 int repeats_ = 0;
368 int my_id_ = 0;
369};
370
371// Comparison of output of this test as tested on a MacBook Pro Retina, 15-inch,
372// Mid 2014, 2,8 GHz Intel Core i7, 16 GB 1600 MHz DDR3,
373// running OS X El Capitan, 10.11.2.
374//
375// Native mutex implementation:
376// Approximate CPU usage:
377// System: ~16%
378// User mode: ~1.3%
379// Idle: ~82%
380// Unit test output:
381// [ OK ] CriticalSectionTest.Performance (234545 ms)
382//
383// Special partially spin lock based implementation:
384// Approximate CPU usage:
385// System: ~75%
386// User mode: ~16%
387// Idle: ~8%
388// Unit test output:
389// [ OK ] CriticalSectionTest.Performance (2107 ms)
390//
391// The test is disabled by default to avoid unecessarily loading the bots.
392TEST(CriticalSectionTest, DISABLED_Performance) {
393 PerfTestThread threads[8];
394 Event event(false, false);
395
396 static const int kThreadRepeats = 10000000;
397 static const int kExpectedCount = kThreadRepeats * arraysize(threads);
398 PerfTestData test_data(kExpectedCount, &event);
399
400 for (auto& t : threads)
401 t.Start(&test_data, kThreadRepeats, 1);
402
403 event.Wait(Event::kForever);
404
405 for (auto& t : threads)
406 t.Stop();
407}
408
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000409} // namespace rtc