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 #ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_H_ 12 #define RTC_BASE_SYNCHRONIZATION_MUTEX_H_ 13 14 #include <atomic> 15 16 #include "absl/base/const_init.h" 17 #include "rtc_base/checks.h" 18 #include "rtc_base/platform_thread_types.h" 19 #include "rtc_base/system/unused.h" 20 #include "rtc_base/thread_annotations.h" 21 22 #if defined(WEBRTC_ABSL_MUTEX) 23 #include "rtc_base/synchronization/mutex_abseil.h" // nogncheck 24 #elif defined(WEBRTC_WIN) 25 #include "rtc_base/synchronization/mutex_critical_section.h" 26 #elif defined(WEBRTC_POSIX) 27 #include "rtc_base/synchronization/mutex_pthread.h" 28 #else 29 #error Unsupported platform. 30 #endif 31 32 namespace webrtc { 33 34 // The Mutex guarantees exclusive access and aims to follow Abseil semantics 35 // (i.e. non-reentrant etc). 36 class RTC_LOCKABLE Mutex final { 37 public: 38 Mutex() = default; 39 Mutex(const Mutex&) = delete; 40 Mutex& operator=(const Mutex&) = delete; 41 Lock()42 void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { 43 rtc::PlatformThreadRef current = CurrentThreadRefAssertingNotBeingHolder(); 44 impl_.Lock(); 45 // |holder_| changes from 0 to CurrentThreadRef(). 46 holder_.store(current, std::memory_order_relaxed); 47 } TryLock()48 RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) { 49 rtc::PlatformThreadRef current = CurrentThreadRefAssertingNotBeingHolder(); 50 if (impl_.TryLock()) { 51 // |holder_| changes from 0 to CurrentThreadRef(). 52 holder_.store(current, std::memory_order_relaxed); 53 return true; 54 } 55 return false; 56 } Unlock()57 void Unlock() RTC_UNLOCK_FUNCTION() { 58 // |holder_| changes from CurrentThreadRef() to 0. If something else than 59 // CurrentThreadRef() is stored in |holder_|, the Unlock results in 60 // undefined behavior as mutexes can't be unlocked from another thread than 61 // the one that locked it, or called while not being locked. 62 holder_.store(0, std::memory_order_relaxed); 63 impl_.Unlock(); 64 } 65 66 private: CurrentThreadRefAssertingNotBeingHolder()67 rtc::PlatformThreadRef CurrentThreadRefAssertingNotBeingHolder() { 68 rtc::PlatformThreadRef holder = holder_.load(std::memory_order_relaxed); 69 rtc::PlatformThreadRef current = rtc::CurrentThreadRef(); 70 // TODO(bugs.webrtc.org/11567): remove this temporary check after migrating 71 // fully to Mutex. 72 RTC_CHECK_NE(holder, current); 73 return current; 74 } 75 76 MutexImpl impl_; 77 // TODO(bugs.webrtc.org/11567): remove |holder_| after migrating fully to 78 // Mutex. 79 // |holder_| contains the PlatformThreadRef of the thread currently holding 80 // the lock, or 0. 81 // Remarks on the used memory orders: the atomic load in 82 // CurrentThreadRefAssertingNotBeingHolder() observes either of two things: 83 // 1. our own previous write to holder_ with our thread ID. 84 // 2. another thread (with ID y) writing y and then 0 from an initial value of 85 // 0. If we're observing case 1, our own stores are obviously ordered before 86 // the load, and hit the CHECK. If we're observing case 2, the value observed 87 // w.r.t |impl_| being locked depends on the memory order. Since we only care 88 // that it's different from CurrentThreadRef()), we use the more performant 89 // option, memory_order_relaxed. 90 std::atomic<rtc::PlatformThreadRef> holder_ = {0}; 91 }; 92 93 // MutexLock, for serializing execution through a scope. 94 class RTC_SCOPED_LOCKABLE MutexLock final { 95 public: 96 MutexLock(const MutexLock&) = delete; 97 MutexLock& operator=(const MutexLock&) = delete; 98 MutexLock(Mutex * mutex)99 explicit MutexLock(Mutex* mutex) RTC_EXCLUSIVE_LOCK_FUNCTION(mutex) 100 : mutex_(mutex) { 101 mutex->Lock(); 102 } RTC_UNLOCK_FUNCTION()103 ~MutexLock() RTC_UNLOCK_FUNCTION() { mutex_->Unlock(); } 104 105 private: 106 Mutex* mutex_; 107 }; 108 109 // A mutex used to protect global variables. Do NOT use for other purposes. 110 #if defined(WEBRTC_ABSL_MUTEX) 111 using GlobalMutex = absl::Mutex; 112 using GlobalMutexLock = absl::MutexLock; 113 #else 114 class RTC_LOCKABLE GlobalMutex final { 115 public: 116 GlobalMutex(const GlobalMutex&) = delete; 117 GlobalMutex& operator=(const GlobalMutex&) = delete; 118 GlobalMutex(absl::ConstInitType)119 constexpr explicit GlobalMutex(absl::ConstInitType /*unused*/) 120 : mutex_locked_(0) {} 121 122 void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION(); 123 void Unlock() RTC_UNLOCK_FUNCTION(); 124 125 private: 126 std::atomic<int> mutex_locked_; // 0 means lock not taken, 1 means taken. 127 }; 128 129 // GlobalMutexLock, for serializing execution through a scope. 130 class RTC_SCOPED_LOCKABLE GlobalMutexLock final { 131 public: 132 GlobalMutexLock(const GlobalMutexLock&) = delete; 133 GlobalMutexLock& operator=(const GlobalMutexLock&) = delete; 134 135 explicit GlobalMutexLock(GlobalMutex* mutex) 136 RTC_EXCLUSIVE_LOCK_FUNCTION(mutex_); 137 ~GlobalMutexLock() RTC_UNLOCK_FUNCTION(); 138 139 private: 140 GlobalMutex* mutex_; 141 }; 142 #endif // if defined(WEBRTC_ABSL_MUTEX) 143 144 } // namespace webrtc 145 146 #endif // RTC_BASE_SYNCHRONIZATION_MUTEX_H_ 147