blob: ea672ecd9c9350364173a48eb5c9458b607c20fc [file] [log] [blame]
Markus Handellf70fbc82020-06-04 00:41:20 +02001/*
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 Handell2d27b1a2020-06-09 17:34:41 +020018#include "rtc_base/platform_thread.h"
Markus Handellf70fbc82020-06-04 00:41:20 +020019#include "rtc_base/system/unused.h"
20#include "rtc_base/thread_annotations.h"
21
22#if defined(WEBRTC_ABSL_MUTEX)
Markus Handell8e75bd42020-06-05 11:47:40 +020023#include "rtc_base/synchronization/mutex_abseil.h" // nogncheck
Markus Handellf70fbc82020-06-04 00:41:20 +020024#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
32namespace webrtc {
33
34// The Mutex guarantees exclusive access and aims to follow Abseil semantics
35// (i.e. non-reentrant etc).
36class RTC_LOCKABLE Mutex final {
37 public:
38 Mutex() = default;
39 Mutex(const Mutex&) = delete;
40 Mutex& operator=(const Mutex&) = delete;
41
Markus Handell2d27b1a2020-06-09 17:34:41 +020042 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 Handellf70fbc82020-06-04 00:41:20 +020047 }
Markus Handell2d27b1a2020-06-09 17:34:41 +020048 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 Handellf70fbc82020-06-04 00:41:20 +020065
66 private:
Markus Handell2d27b1a2020-06-09 17:34:41 +020067 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 Handellf70fbc82020-06-04 00:41:20 +020076 MutexImpl impl_;
Markus Handell2d27b1a2020-06-09 17:34:41 +020077 // 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 Handellf70fbc82020-06-04 00:41:20 +020091};
92
93// MutexLock, for serializing execution through a scope.
94class 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)
111using GlobalMutex = absl::Mutex;
112using GlobalMutexLock = absl::MutexLock;
113#else
114class 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.
130class 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_