Markus Handell | f70fbc8 | 2020-06-04 00:41:20 +0200 | [diff] [blame] | 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" |
Markus Handell | 2d27b1a | 2020-06-09 17:34:41 +0200 | [diff] [blame] | 18 | #include "rtc_base/platform_thread.h" |
Markus Handell | f70fbc8 | 2020-06-04 00:41:20 +0200 | [diff] [blame] | 19 | #include "rtc_base/system/unused.h" |
| 20 | #include "rtc_base/thread_annotations.h" |
| 21 | |
| 22 | #if defined(WEBRTC_ABSL_MUTEX) |
Markus Handell | 8e75bd4 | 2020-06-05 11:47:40 +0200 | [diff] [blame] | 23 | #include "rtc_base/synchronization/mutex_abseil.h" // nogncheck |
Markus Handell | f70fbc8 | 2020-06-04 00:41:20 +0200 | [diff] [blame] | 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 | |
Markus Handell | 2d27b1a | 2020-06-09 17:34:41 +0200 | [diff] [blame] | 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); |
Markus Handell | f70fbc8 | 2020-06-04 00:41:20 +0200 | [diff] [blame] | 47 | } |
Markus Handell | 2d27b1a | 2020-06-09 17:34:41 +0200 | [diff] [blame] | 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 | } |
| 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 | } |
Markus Handell | f70fbc8 | 2020-06-04 00:41:20 +0200 | [diff] [blame] | 65 | |
| 66 | private: |
Markus Handell | 2d27b1a | 2020-06-09 17:34:41 +0200 | [diff] [blame] | 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 | |
Markus Handell | f70fbc8 | 2020-06-04 00:41:20 +0200 | [diff] [blame] | 76 | MutexImpl impl_; |
Markus Handell | 2d27b1a | 2020-06-09 17:34:41 +0200 | [diff] [blame] | 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}; |
Markus Handell | f70fbc8 | 2020-06-04 00:41:20 +0200 | [diff] [blame] | 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 | |
| 99 | explicit MutexLock(Mutex* mutex) RTC_EXCLUSIVE_LOCK_FUNCTION(mutex) |
| 100 | : mutex_(mutex) { |
| 101 | mutex->Lock(); |
| 102 | } |
| 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 | |
| 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) RTC_EXCLUSIVE_LOCK_FUNCTION(); |
| 136 | ~GlobalMutexLock() RTC_UNLOCK_FUNCTION(); |
| 137 | |
| 138 | private: |
| 139 | GlobalMutex* mutex_; |
| 140 | }; |
| 141 | #endif // if defined(WEBRTC_ABSL_MUTEX) |
| 142 | |
| 143 | } // namespace webrtc |
| 144 | |
| 145 | #endif // RTC_BASE_SYNCHRONIZATION_MUTEX_H_ |