1 /*
2  *  Copyright 2020 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 
11 #include "rtc_base/synchronization/mutex.h"
12 
13 #include <stddef.h>
14 #include <stdint.h>
15 
16 #include <atomic>
17 #include <memory>
18 #include <type_traits>
19 #include <utility>
20 #include <vector>
21 
22 #include "benchmark/benchmark.h"
23 #include "rtc_base/checks.h"
24 #include "rtc_base/event.h"
25 #include "rtc_base/location.h"
26 #include "rtc_base/message_handler.h"
27 #include "rtc_base/platform_thread.h"
28 #include "rtc_base/synchronization/yield.h"
29 #include "rtc_base/thread.h"
30 #include "test/gtest.h"
31 
32 namespace webrtc {
33 namespace {
34 
35 using ::rtc::Event;
36 using ::rtc::Message;
37 using ::rtc::MessageHandler;
38 using ::rtc::Thread;
39 
40 constexpr int kNumThreads = 16;
41 
42 template <class MutexType>
43 class RTC_LOCKABLE RawMutexLocker {
44  public:
RawMutexLocker(MutexType & mutex)45   explicit RawMutexLocker(MutexType& mutex) : mutex_(mutex) {}
Lock()46   void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { mutex_.Lock(); }
Unlock()47   void Unlock() RTC_UNLOCK_FUNCTION() { mutex_.Unlock(); }
48 
49  private:
50   MutexType& mutex_;
51 };
52 
53 class RTC_LOCKABLE RawMutexTryLocker {
54  public:
RawMutexTryLocker(Mutex & mutex)55   explicit RawMutexTryLocker(Mutex& mutex) : mutex_(mutex) {}
Lock()56   void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() {
57     while (!mutex_.TryLock()) {
58       YieldCurrentThread();
59     }
60   }
Unlock()61   void Unlock() RTC_UNLOCK_FUNCTION() { mutex_.Unlock(); }
62 
63  private:
64   Mutex& mutex_;
65 };
66 
67 template <class MutexType, class MutexLockType>
68 class MutexLockLocker {
69  public:
MutexLockLocker(MutexType & mutex)70   explicit MutexLockLocker(MutexType& mutex) : mutex_(mutex) {}
Lock()71   void Lock() { lock_ = std::make_unique<MutexLockType>(&mutex_); }
Unlock()72   void Unlock() { lock_ = nullptr; }
73 
74  private:
75   MutexType& mutex_;
76   std::unique_ptr<MutexLockType> lock_;
77 };
78 
79 template <class MutexType, class MutexLocker>
80 class LockRunner : public rtc::MessageHandlerAutoCleanup {
81  public:
82   template <typename... Args>
LockRunner(Args...args)83   explicit LockRunner(Args... args)
84       : threads_active_(0),
85         start_event_(true, false),
86         done_event_(true, false),
87         shared_value_(0),
88         mutex_(args...),
89         locker_(mutex_) {}
90 
Run()91   bool Run() {
92     // Signal all threads to start.
93     start_event_.Set();
94 
95     // Wait for all threads to finish.
96     return done_event_.Wait(kLongTime);
97   }
98 
SetExpectedThreadCount(int count)99   void SetExpectedThreadCount(int count) { threads_active_ = count; }
100 
shared_value()101   int shared_value() {
102     int shared_value;
103     locker_.Lock();
104     shared_value = shared_value_;
105     locker_.Unlock();
106     return shared_value_;
107   }
108 
OnMessage(Message * msg)109   void OnMessage(Message* msg) override {
110     ASSERT_TRUE(start_event_.Wait(kLongTime));
111     locker_.Lock();
112 
113     EXPECT_EQ(0, shared_value_);
114     int old = shared_value_;
115 
116     // Use a loop to increase the chance of race. If the |locker_|
117     // implementation is faulty, it would be improbable that the error slips
118     // through.
119     for (int i = 0; i < kOperationsToRun; ++i) {
120       benchmark::DoNotOptimize(++shared_value_);
121     }
122     EXPECT_EQ(old + kOperationsToRun, shared_value_);
123     shared_value_ = 0;
124 
125     locker_.Unlock();
126     if (threads_active_.fetch_sub(1) == 1) {
127       done_event_.Set();
128     }
129   }
130 
131  private:
132   static constexpr int kLongTime = 10000;  // 10 seconds
133   static constexpr int kOperationsToRun = 1000;
134 
135   std::atomic<int> threads_active_;
136   Event start_event_;
137   Event done_event_;
138   int shared_value_;
139   MutexType mutex_;
140   MutexLocker locker_;
141 };
142 
StartThreads(std::vector<std::unique_ptr<Thread>> & threads,MessageHandler * handler)143 void StartThreads(std::vector<std::unique_ptr<Thread>>& threads,
144                   MessageHandler* handler) {
145   for (int i = 0; i < kNumThreads; ++i) {
146     std::unique_ptr<Thread> thread(Thread::Create());
147     thread->Start();
148     thread->Post(RTC_FROM_HERE, handler);
149     threads.push_back(std::move(thread));
150   }
151 }
152 
TEST(MutexTest,ProtectsSharedResourceWithMutexAndRawMutexLocker)153 TEST(MutexTest, ProtectsSharedResourceWithMutexAndRawMutexLocker) {
154   std::vector<std::unique_ptr<Thread>> threads;
155   LockRunner<Mutex, RawMutexLocker<Mutex>> runner;
156   StartThreads(threads, &runner);
157   runner.SetExpectedThreadCount(kNumThreads);
158   EXPECT_TRUE(runner.Run());
159   EXPECT_EQ(0, runner.shared_value());
160 }
161 
TEST(MutexTest,ProtectsSharedResourceWithMutexAndRawMutexTryLocker)162 TEST(MutexTest, ProtectsSharedResourceWithMutexAndRawMutexTryLocker) {
163   std::vector<std::unique_ptr<Thread>> threads;
164   LockRunner<Mutex, RawMutexTryLocker> runner;
165   StartThreads(threads, &runner);
166   runner.SetExpectedThreadCount(kNumThreads);
167   EXPECT_TRUE(runner.Run());
168   EXPECT_EQ(0, runner.shared_value());
169 }
170 
TEST(MutexTest,ProtectsSharedResourceWithMutexAndMutexLocker)171 TEST(MutexTest, ProtectsSharedResourceWithMutexAndMutexLocker) {
172   std::vector<std::unique_ptr<Thread>> threads;
173   LockRunner<Mutex, MutexLockLocker<Mutex, MutexLock>> runner;
174   StartThreads(threads, &runner);
175   runner.SetExpectedThreadCount(kNumThreads);
176   EXPECT_TRUE(runner.Run());
177   EXPECT_EQ(0, runner.shared_value());
178 }
179 
TEST(MutexTest,ProtectsSharedResourceWithGlobalMutexAndRawMutexLocker)180 TEST(MutexTest, ProtectsSharedResourceWithGlobalMutexAndRawMutexLocker) {
181   std::vector<std::unique_ptr<Thread>> threads;
182   LockRunner<GlobalMutex, RawMutexLocker<GlobalMutex>> runner(absl::kConstInit);
183   StartThreads(threads, &runner);
184   runner.SetExpectedThreadCount(kNumThreads);
185   EXPECT_TRUE(runner.Run());
186   EXPECT_EQ(0, runner.shared_value());
187 }
188 
TEST(MutexTest,ProtectsSharedResourceWithGlobalMutexAndMutexLocker)189 TEST(MutexTest, ProtectsSharedResourceWithGlobalMutexAndMutexLocker) {
190   std::vector<std::unique_ptr<Thread>> threads;
191   LockRunner<GlobalMutex, MutexLockLocker<GlobalMutex, GlobalMutexLock>> runner(
192       absl::kConstInit);
193   StartThreads(threads, &runner);
194   runner.SetExpectedThreadCount(kNumThreads);
195   EXPECT_TRUE(runner.Run());
196   EXPECT_EQ(0, runner.shared_value());
197 }
198 
TEST(MutexTest,GlobalMutexCanHaveStaticStorageDuration)199 TEST(MutexTest, GlobalMutexCanHaveStaticStorageDuration) {
200   ABSL_CONST_INIT static GlobalMutex global_lock(absl::kConstInit);
201   global_lock.Lock();
202   global_lock.Unlock();
203 }
204 
205 }  // namespace
206 }  // namespace webrtc
207