• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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