aboutsummaryrefslogtreecommitdiff
path: root/rtc_base
diff options
context:
space:
mode:
authorMarkus Handell <handellm@webrtc.org>2020-06-04 00:41:20 +0200
committerCommit Bot <commit-bot@chromium.org>2020-06-04 09:55:12 +0000
commitf70fbc841166d397c4acf4bebae5d3a0c32727f8 (patch)
treecc5159fbd720487ab853e4985529c066d7709675 /rtc_base
parente917379c5b15827f26ab0bdd37abebd0c6a56ff2 (diff)
downloadwebrtc-f70fbc841166d397c4acf4bebae5d3a0c32727f8.tar.gz
Introduces rtc_base/synchronization/mutex.h.
This change introduces a new non-reentrant mutex to WebRTC. It enables eventual migration to Abseil's mutex. The mutex types supportable by webrtc::Mutex are - absl::Mutex - CriticalSection (Windows only) - pthread_mutex (POSIX only) In addition to introducing the mutexes, the CL also changes PacketBuffer to use the new mutex instead of rtc::CriticalSection. The method of yielding from critical_section.cc was given a mini-cleanup and YieldCurrentThread() was added to rtc_base/synchronization/yield.h/cc. Additionally, google_benchmark benchmarks for the mutexes were added (test courtesy of danilchap@), and some results from a pthread/Abseil shootout were added showing Abseil has the advantage in higher contention. Bug: webrtc:11567, webrtc:11634 Change-Id: Iaec324ccb32ec3851bf6db3fd290f5ea5dee4c81 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/176230 Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Reviewed-by: Tommi <tommi@webrtc.org> Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31443}
Diffstat (limited to 'rtc_base')
-rw-r--r--rtc_base/BUILD.gn1
-rw-r--r--rtc_base/DEPS2
-rw-r--r--rtc_base/critical_section.cc14
-rw-r--r--rtc_base/synchronization/BUILD.gn47
-rw-r--r--rtc_base/synchronization/DEPS11
-rw-r--r--rtc_base/synchronization/mutex.cc39
-rw-r--r--rtc_base/synchronization/mutex.h103
-rw-r--r--rtc_base/synchronization/mutex_abseil.h37
-rw-r--r--rtc_base/synchronization/mutex_benchmark.cc95
-rw-r--r--rtc_base/synchronization/mutex_critical_section.h54
-rw-r--r--rtc_base/synchronization/mutex_pthread.h53
-rw-r--r--rtc_base/synchronization/mutex_unittest.cc206
-rw-r--r--rtc_base/synchronization/yield.cc36
-rw-r--r--rtc_base/synchronization/yield.h20
14 files changed, 704 insertions, 14 deletions
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 3436bb3e06..f7155a825a 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -177,6 +177,7 @@ rtc_library("criticalsection") {
":checks",
":macromagic",
":platform_thread_types",
+ "synchronization:yield",
"system:rtc_export",
"system:unused",
]
diff --git a/rtc_base/DEPS b/rtc_base/DEPS
index 679d06dfc8..c9f7dc5898 100644
--- a/rtc_base/DEPS
+++ b/rtc_base/DEPS
@@ -1,8 +1,8 @@
include_rules = [
"+base/third_party/libevent",
"+json",
- "+third_party/jsoncpp",
"+system_wrappers",
+ "+third_party/jsoncpp",
]
specific_include_rules = {
diff --git a/rtc_base/critical_section.cc b/rtc_base/critical_section.cc
index 1969edefa5..4fad961209 100644
--- a/rtc_base/critical_section.cc
+++ b/rtc_base/critical_section.cc
@@ -15,6 +15,7 @@
#include "rtc_base/atomic_ops.h"
#include "rtc_base/checks.h"
#include "rtc_base/platform_thread_types.h"
+#include "rtc_base/synchronization/yield.h"
#include "rtc_base/system/unused.h"
// TODO(tommi): Split this file up to per-platform implementation files.
@@ -217,19 +218,8 @@ CritScope::~CritScope() {
}
void GlobalLock::Lock() {
-#if !defined(WEBRTC_WIN) && \
- (!defined(WEBRTC_MAC) || RTC_USE_NATIVE_MUTEX_ON_MAC)
- const struct timespec ts_null = {0};
-#endif
-
while (AtomicOps::CompareAndSwap(&lock_acquired_, 0, 1)) {
-#if defined(WEBRTC_WIN)
- ::Sleep(0);
-#elif defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
- sched_yield();
-#else
- nanosleep(&ts_null, nullptr);
-#endif
+ webrtc::YieldCurrentThread();
}
}
diff --git a/rtc_base/synchronization/BUILD.gn b/rtc_base/synchronization/BUILD.gn
index 3e7b22d4f9..b1942e7815 100644
--- a/rtc_base/synchronization/BUILD.gn
+++ b/rtc_base/synchronization/BUILD.gn
@@ -11,6 +11,33 @@ if (is_android) {
import("//build/config/android/config.gni")
import("//build/config/android/rules.gni")
}
+import("//third_party/google_benchmark/buildconfig.gni")
+
+rtc_library("yield") {
+ sources = [
+ "yield.cc",
+ "yield.h",
+ ]
+ deps = []
+}
+
+rtc_library("mutex") {
+ sources = [
+ "mutex.cc",
+ "mutex.h",
+ "mutex_abseil.h",
+ "mutex_critical_section.h",
+ "mutex_pthread.h",
+ ]
+ deps = [
+ ":yield",
+ "..:checks",
+ "..:macromagic",
+ "../system:unused",
+ "//third_party/abseil-cpp/absl/base:core_headers",
+ "//third_party/abseil-cpp/absl/synchronization",
+ ]
+}
rtc_library("rw_lock_wrapper") {
public = [ "rw_lock_wrapper.h" ]
@@ -60,11 +87,29 @@ rtc_library("yield_policy") {
if (rtc_include_tests) {
rtc_library("synchronization_unittests") {
testonly = true
- sources = [ "yield_policy_unittest.cc" ]
+ sources = [
+ "mutex_unittest.cc",
+ "yield_policy_unittest.cc",
+ ]
deps = [
+ ":mutex",
+ ":yield",
":yield_policy",
+ "..:checks",
+ "..:macromagic",
+ "..:rtc_base",
"..:rtc_event",
"../../test:test_support",
+ "//third_party/google_benchmark",
+ ]
+ }
+
+ rtc_library("mutex_benchmark") {
+ testonly = true
+ sources = [ "mutex_benchmark.cc" ]
+ deps = [
+ ":mutex",
+ "//third_party/google_benchmark",
]
}
diff --git a/rtc_base/synchronization/DEPS b/rtc_base/synchronization/DEPS
new file mode 100644
index 0000000000..4ed1f2444b
--- /dev/null
+++ b/rtc_base/synchronization/DEPS
@@ -0,0 +1,11 @@
+specific_include_rules = {
+ "mutex_abseil\.h": [
+ "+absl/synchronization"
+ ],
+ ".*_benchmark\.cc": [
+ "+benchmark",
+ ],
+ ".*_unittest\.cc": [
+ "+benchmark",
+ ]
+}
diff --git a/rtc_base/synchronization/mutex.cc b/rtc_base/synchronization/mutex.cc
new file mode 100644
index 0000000000..6c2d6ff7f0
--- /dev/null
+++ b/rtc_base/synchronization/mutex.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/synchronization/mutex.h"
+
+#include "rtc_base/checks.h"
+#include "rtc_base/synchronization/yield.h"
+
+namespace webrtc {
+
+#if !defined(WEBRTC_ABSL_MUTEX)
+void GlobalMutex::Lock() {
+ while (mutex_locked_.exchange(1)) {
+ YieldCurrentThread();
+ }
+}
+
+void GlobalMutex::Unlock() {
+ int old = mutex_locked_.exchange(0);
+ RTC_DCHECK_EQ(old, 1) << "Unlock called without calling Lock first";
+}
+
+GlobalMutexLock::GlobalMutexLock(GlobalMutex* mutex) : mutex_(mutex) {
+ mutex_->Lock();
+}
+
+GlobalMutexLock::~GlobalMutexLock() {
+ mutex_->Unlock();
+}
+#endif // #if !defined(WEBRTC_ABSL_MUTEX)
+
+} // namespace webrtc
diff --git a/rtc_base/synchronization/mutex.h b/rtc_base/synchronization/mutex.h
new file mode 100644
index 0000000000..bfcadc2139
--- /dev/null
+++ b/rtc_base/synchronization/mutex.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_H_
+#define RTC_BASE_SYNCHRONIZATION_MUTEX_H_
+
+#include <atomic>
+
+#include "absl/base/const_init.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/system/unused.h"
+#include "rtc_base/thread_annotations.h"
+
+#if defined(WEBRTC_ABSL_MUTEX)
+#include "rtc_base/synchronization/mutex_abseil.h"
+#elif defined(WEBRTC_WIN)
+#include "rtc_base/synchronization/mutex_critical_section.h"
+#elif defined(WEBRTC_POSIX)
+#include "rtc_base/synchronization/mutex_pthread.h"
+#else
+#error Unsupported platform.
+#endif
+
+namespace webrtc {
+
+// The Mutex guarantees exclusive access and aims to follow Abseil semantics
+// (i.e. non-reentrant etc).
+class RTC_LOCKABLE Mutex final {
+ public:
+ Mutex() = default;
+ Mutex(const Mutex&) = delete;
+ Mutex& operator=(const Mutex&) = delete;
+
+ void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { impl_.Lock(); }
+ RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+ return impl_.TryLock();
+ }
+ void Unlock() RTC_UNLOCK_FUNCTION() { impl_.Unlock(); }
+
+ private:
+ MutexImpl impl_;
+};
+
+// MutexLock, for serializing execution through a scope.
+class RTC_SCOPED_LOCKABLE MutexLock final {
+ public:
+ MutexLock(const MutexLock&) = delete;
+ MutexLock& operator=(const MutexLock&) = delete;
+
+ explicit MutexLock(Mutex* mutex) RTC_EXCLUSIVE_LOCK_FUNCTION(mutex)
+ : mutex_(mutex) {
+ mutex->Lock();
+ }
+ ~MutexLock() RTC_UNLOCK_FUNCTION() { mutex_->Unlock(); }
+
+ private:
+ Mutex* mutex_;
+};
+
+// A mutex used to protect global variables. Do NOT use for other purposes.
+#if defined(WEBRTC_ABSL_MUTEX)
+using GlobalMutex = absl::Mutex;
+using GlobalMutexLock = absl::MutexLock;
+#else
+class RTC_LOCKABLE GlobalMutex final {
+ public:
+ GlobalMutex(const GlobalMutex&) = delete;
+ GlobalMutex& operator=(const GlobalMutex&) = delete;
+
+ constexpr explicit GlobalMutex(absl::ConstInitType /*unused*/)
+ : mutex_locked_(0) {}
+
+ void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION();
+ void Unlock() RTC_UNLOCK_FUNCTION();
+
+ private:
+ std::atomic<int> mutex_locked_; // 0 means lock not taken, 1 means taken.
+};
+
+// GlobalMutexLock, for serializing execution through a scope.
+class RTC_SCOPED_LOCKABLE GlobalMutexLock final {
+ public:
+ GlobalMutexLock(const GlobalMutexLock&) = delete;
+ GlobalMutexLock& operator=(const GlobalMutexLock&) = delete;
+
+ explicit GlobalMutexLock(GlobalMutex* mutex) RTC_EXCLUSIVE_LOCK_FUNCTION();
+ ~GlobalMutexLock() RTC_UNLOCK_FUNCTION();
+
+ private:
+ GlobalMutex* mutex_;
+};
+#endif // if defined(WEBRTC_ABSL_MUTEX)
+
+} // namespace webrtc
+
+#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_H_
diff --git a/rtc_base/synchronization/mutex_abseil.h b/rtc_base/synchronization/mutex_abseil.h
new file mode 100644
index 0000000000..4ad1d07eef
--- /dev/null
+++ b/rtc_base/synchronization/mutex_abseil.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_ABSEIL_H_
+#define RTC_BASE_SYNCHRONIZATION_MUTEX_ABSEIL_H_
+
+#include "absl/synchronization/mutex.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace webrtc {
+
+class RTC_LOCKABLE MutexImpl final {
+ public:
+ MutexImpl() = default;
+ MutexImpl(const MutexImpl&) = delete;
+ MutexImpl& operator=(const MutexImpl&) = delete;
+
+ void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { mutex_.Lock(); }
+ RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+ return mutex_.TryLock();
+ }
+ void Unlock() RTC_UNLOCK_FUNCTION() { mutex_.Unlock(); }
+
+ private:
+ absl::Mutex mutex_;
+};
+
+} // namespace webrtc
+
+#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_ABSEIL_H_
diff --git a/rtc_base/synchronization/mutex_benchmark.cc b/rtc_base/synchronization/mutex_benchmark.cc
new file mode 100644
index 0000000000..ee3e3af033
--- /dev/null
+++ b/rtc_base/synchronization/mutex_benchmark.cc
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "benchmark/benchmark.h"
+#include "rtc_base/synchronization/mutex.h"
+
+namespace webrtc {
+
+class PerfTestData {
+ public:
+ PerfTestData() : cache_line_barrier_1_(), cache_line_barrier_2_() {
+ cache_line_barrier_1_[0]++; // Avoid 'is not used'.
+ cache_line_barrier_2_[0]++; // Avoid 'is not used'.
+ }
+
+ int AddToCounter(int add) {
+ MutexLock mu(&mu_);
+ my_counter_ += add;
+ return 0;
+ }
+
+ private:
+ uint8_t cache_line_barrier_1_[64];
+ Mutex mu_;
+ uint8_t cache_line_barrier_2_[64];
+ int64_t my_counter_ = 0;
+};
+
+// TODO(bugs.webrtc.org/11630): remove NOLINT when linter supports mutable
+// references.
+void BM_LockWithMutex(benchmark::State& state) { // NOLINT
+ static PerfTestData test_data;
+ for (auto s : state) {
+ benchmark::DoNotOptimize(test_data.AddToCounter(2));
+ }
+}
+
+BENCHMARK(BM_LockWithMutex)->Threads(1);
+BENCHMARK(BM_LockWithMutex)->Threads(2);
+BENCHMARK(BM_LockWithMutex)->Threads(4);
+BENCHMARK(BM_LockWithMutex)->ThreadPerCpu();
+
+} // namespace webrtc
+
+/*
+
+Results:
+
+NB when reproducing: Remember to turn of power management features such as CPU
+scaling before running!
+
+pthreads (Linux):
+----------------------------------------------------------------------
+Run on (12 X 4500 MHz CPU s)
+CPU Caches:
+ L1 Data 32 KiB (x6)
+ L1 Instruction 32 KiB (x6)
+ L2 Unified 1024 KiB (x6)
+ L3 Unified 8448 KiB (x1)
+Load Average: 0.26, 0.28, 0.44
+----------------------------------------------------------------------
+Benchmark Time CPU Iterations
+----------------------------------------------------------------------
+BM_LockWithMutex/threads:1 13.4 ns 13.4 ns 52192906
+BM_LockWithMutex/threads:2 44.2 ns 88.4 ns 8189944
+BM_LockWithMutex/threads:4 52.0 ns 198 ns 3743244
+BM_LockWithMutex/threads:12 84.9 ns 944 ns 733524
+
+std::mutex performs like the pthread implementation (Linux).
+
+Abseil (Linux):
+----------------------------------------------------------------------
+Run on (12 X 4500 MHz CPU s)
+CPU Caches:
+ L1 Data 32 KiB (x6)
+ L1 Instruction 32 KiB (x6)
+ L2 Unified 1024 KiB (x6)
+ L3 Unified 8448 KiB (x1)
+Load Average: 0.27, 0.24, 0.37
+----------------------------------------------------------------------
+Benchmark Time CPU Iterations
+----------------------------------------------------------------------
+BM_LockWithMutex/threads:1 15.0 ns 15.0 ns 46550231
+BM_LockWithMutex/threads:2 91.1 ns 182 ns 4059212
+BM_LockWithMutex/threads:4 40.8 ns 131 ns 5496560
+BM_LockWithMutex/threads:12 37.0 ns 130 ns 5377668
+
+*/
diff --git a/rtc_base/synchronization/mutex_critical_section.h b/rtc_base/synchronization/mutex_critical_section.h
new file mode 100644
index 0000000000..d206794988
--- /dev/null
+++ b/rtc_base/synchronization/mutex_critical_section.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_CRITICAL_SECTION_H_
+#define RTC_BASE_SYNCHRONIZATION_MUTEX_CRITICAL_SECTION_H_
+
+#if defined(WEBRTC_WIN)
+// clang-format off
+// clang formating would change include order.
+
+// Include winsock2.h before including <windows.h> to maintain consistency with
+// win32.h. To include win32.h directly, it must be broken out into its own
+// build target.
+#include <winsock2.h>
+#include <windows.h>
+#include <sal.h> // must come after windows headers.
+// clang-format on
+
+#include "rtc_base/thread_annotations.h"
+
+namespace webrtc {
+
+class RTC_LOCKABLE MutexImpl final {
+ public:
+ MutexImpl() { InitializeCriticalSection(&critical_section_); }
+ MutexImpl(const MutexImpl&) = delete;
+ MutexImpl& operator=(const MutexImpl&) = delete;
+ ~MutexImpl() { DeleteCriticalSection(&critical_section_); }
+
+ void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() {
+ EnterCriticalSection(&critical_section_);
+ }
+ RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+ return TryEnterCriticalSection(&critical_section_) != FALSE;
+ }
+ void Unlock() RTC_UNLOCK_FUNCTION() {
+ LeaveCriticalSection(&critical_section_);
+ }
+
+ private:
+ CRITICAL_SECTION critical_section_;
+};
+
+} // namespace webrtc
+
+#endif // #if defined(WEBRTC_WIN)
+#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_CRITICAL_SECTION_H_
diff --git a/rtc_base/synchronization/mutex_pthread.h b/rtc_base/synchronization/mutex_pthread.h
new file mode 100644
index 0000000000..c3d9a7aff0
--- /dev/null
+++ b/rtc_base/synchronization/mutex_pthread.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_SYNCHRONIZATION_MUTEX_PTHREAD_H_
+#define RTC_BASE_SYNCHRONIZATION_MUTEX_PTHREAD_H_
+
+#if defined(WEBRTC_POSIX)
+
+#include <pthread.h>
+#if defined(WEBRTC_MAC)
+#include <pthread_spis.h>
+#endif
+
+#include "rtc_base/thread_annotations.h"
+
+namespace webrtc {
+
+class RTC_LOCKABLE MutexImpl final {
+ public:
+ MutexImpl() {
+ pthread_mutexattr_t mutex_attribute;
+ pthread_mutexattr_init(&mutex_attribute);
+#if defined(WEBRTC_MAC)
+ pthread_mutexattr_setpolicy_np(&mutex_attribute,
+ _PTHREAD_MUTEX_POLICY_FAIRSHARE);
+#endif
+ pthread_mutex_init(&mutex_, &mutex_attribute);
+ pthread_mutexattr_destroy(&mutex_attribute);
+ }
+ MutexImpl(const MutexImpl&) = delete;
+ MutexImpl& operator=(const MutexImpl&) = delete;
+ ~MutexImpl() { pthread_mutex_destroy(&mutex_); }
+
+ void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { pthread_mutex_lock(&mutex_); }
+ RTC_WARN_UNUSED_RESULT bool TryLock() RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
+ return pthread_mutex_trylock(&mutex_) == 0;
+ }
+ void Unlock() RTC_UNLOCK_FUNCTION() { pthread_mutex_unlock(&mutex_); }
+
+ private:
+ pthread_mutex_t mutex_;
+};
+
+} // namespace webrtc
+#endif // #if defined(WEBRTC_POSIX)
+#endif // RTC_BASE_SYNCHRONIZATION_MUTEX_PTHREAD_H_
diff --git a/rtc_base/synchronization/mutex_unittest.cc b/rtc_base/synchronization/mutex_unittest.cc
new file mode 100644
index 0000000000..6a930bc042
--- /dev/null
+++ b/rtc_base/synchronization/mutex_unittest.cc
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/synchronization/mutex.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <atomic>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "benchmark/benchmark.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/event.h"
+#include "rtc_base/location.h"
+#include "rtc_base/message_handler.h"
+#include "rtc_base/platform_thread.h"
+#include "rtc_base/synchronization/yield.h"
+#include "rtc_base/thread.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+using ::rtc::Event;
+using ::rtc::Message;
+using ::rtc::MessageHandler;
+using ::rtc::Thread;
+
+constexpr int kNumThreads = 16;
+
+template <class MutexType>
+class RTC_LOCKABLE RawMutexLocker {
+ public:
+ explicit RawMutexLocker(MutexType& mutex) : mutex_(mutex) {}
+ void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() { mutex_.Lock(); }
+ void Unlock() RTC_UNLOCK_FUNCTION() { mutex_.Unlock(); }
+
+ private:
+ MutexType& mutex_;
+};
+
+class RTC_LOCKABLE RawMutexTryLocker {
+ public:
+ explicit RawMutexTryLocker(Mutex& mutex) : mutex_(mutex) {}
+ void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION() {
+ while (!mutex_.TryLock()) {
+ YieldCurrentThread();
+ }
+ }
+ void Unlock() RTC_UNLOCK_FUNCTION() { mutex_.Unlock(); }
+
+ private:
+ Mutex& mutex_;
+};
+
+template <class MutexType, class MutexLockType>
+class MutexLockLocker {
+ public:
+ explicit MutexLockLocker(MutexType& mutex) : mutex_(mutex) {}
+ void Lock() { lock_ = std::make_unique<MutexLockType>(&mutex_); }
+ void Unlock() { lock_ = nullptr; }
+
+ private:
+ MutexType& mutex_;
+ std::unique_ptr<MutexLockType> lock_;
+};
+
+template <class MutexType, class MutexLocker>
+class LockRunner : public MessageHandler {
+ public:
+ template <typename... Args>
+ explicit LockRunner(Args... args)
+ : threads_active_(0),
+ start_event_(true, false),
+ done_event_(true, false),
+ shared_value_(0),
+ mutex_(args...),
+ locker_(mutex_) {}
+
+ bool Run() {
+ // Signal all threads to start.
+ start_event_.Set();
+
+ // Wait for all threads to finish.
+ return done_event_.Wait(kLongTime);
+ }
+
+ void SetExpectedThreadCount(int count) { threads_active_ = count; }
+
+ int shared_value() {
+ int shared_value;
+ locker_.Lock();
+ shared_value = shared_value_;
+ locker_.Unlock();
+ return shared_value_;
+ }
+
+ void OnMessage(Message* msg) override {
+ ASSERT_TRUE(start_event_.Wait(kLongTime));
+ locker_.Lock();
+
+ EXPECT_EQ(0, shared_value_);
+ int old = shared_value_;
+
+ // Use a loop to increase the chance of race. If the |locker_|
+ // implementation is faulty, it would be improbable that the error slips
+ // through.
+ for (int i = 0; i < kOperationsToRun; ++i) {
+ benchmark::DoNotOptimize(++shared_value_);
+ }
+ EXPECT_EQ(old + kOperationsToRun, shared_value_);
+ shared_value_ = 0;
+
+ locker_.Unlock();
+ if (threads_active_.fetch_sub(1) == 1) {
+ done_event_.Set();
+ }
+ }
+
+ private:
+ static constexpr int kLongTime = 10000; // 10 seconds
+ static constexpr int kOperationsToRun = 1000;
+
+ std::atomic<int> threads_active_;
+ Event start_event_;
+ Event done_event_;
+ int shared_value_;
+ MutexType mutex_;
+ MutexLocker locker_;
+};
+
+void StartThreads(std::vector<std::unique_ptr<Thread>>& threads,
+ MessageHandler* handler) {
+ for (int i = 0; i < kNumThreads; ++i) {
+ std::unique_ptr<Thread> thread(Thread::Create());
+ thread->Start();
+ thread->Post(RTC_FROM_HERE, handler);
+ threads.push_back(std::move(thread));
+ }
+}
+
+TEST(MutexTest, ProtectsSharedResourceWithMutexAndRawMutexLocker) {
+ std::vector<std::unique_ptr<Thread>> threads;
+ LockRunner<Mutex, RawMutexLocker<Mutex>> runner;
+ StartThreads(threads, &runner);
+ runner.SetExpectedThreadCount(kNumThreads);
+ EXPECT_TRUE(runner.Run());
+ EXPECT_EQ(0, runner.shared_value());
+}
+
+TEST(MutexTest, ProtectsSharedResourceWithMutexAndRawMutexTryLocker) {
+ std::vector<std::unique_ptr<Thread>> threads;
+ LockRunner<Mutex, RawMutexTryLocker> runner;
+ StartThreads(threads, &runner);
+ runner.SetExpectedThreadCount(kNumThreads);
+ EXPECT_TRUE(runner.Run());
+ EXPECT_EQ(0, runner.shared_value());
+}
+
+TEST(MutexTest, ProtectsSharedResourceWithMutexAndMutexLocker) {
+ std::vector<std::unique_ptr<Thread>> threads;
+ LockRunner<Mutex, MutexLockLocker<Mutex, MutexLock>> runner;
+ StartThreads(threads, &runner);
+ runner.SetExpectedThreadCount(kNumThreads);
+ EXPECT_TRUE(runner.Run());
+ EXPECT_EQ(0, runner.shared_value());
+}
+
+TEST(MutexTest, ProtectsSharedResourceWithGlobalMutexAndRawMutexLocker) {
+ std::vector<std::unique_ptr<Thread>> threads;
+ LockRunner<GlobalMutex, RawMutexLocker<GlobalMutex>> runner(absl::kConstInit);
+ StartThreads(threads, &runner);
+ runner.SetExpectedThreadCount(kNumThreads);
+ EXPECT_TRUE(runner.Run());
+ EXPECT_EQ(0, runner.shared_value());
+}
+
+TEST(MutexTest, ProtectsSharedResourceWithGlobalMutexAndMutexLocker) {
+ std::vector<std::unique_ptr<Thread>> threads;
+ LockRunner<GlobalMutex, MutexLockLocker<GlobalMutex, GlobalMutexLock>> runner(
+ absl::kConstInit);
+ StartThreads(threads, &runner);
+ runner.SetExpectedThreadCount(kNumThreads);
+ EXPECT_TRUE(runner.Run());
+ EXPECT_EQ(0, runner.shared_value());
+}
+
+TEST(MutexTest, GlobalMutexCanHaveStaticStorageDuration) {
+ ABSL_CONST_INIT static GlobalMutex global_lock(absl::kConstInit);
+ global_lock.Lock();
+ global_lock.Unlock();
+}
+
+} // namespace
+} // namespace webrtc
diff --git a/rtc_base/synchronization/yield.cc b/rtc_base/synchronization/yield.cc
new file mode 100644
index 0000000000..cbb58d12ab
--- /dev/null
+++ b/rtc_base/synchronization/yield.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/synchronization/yield.h"
+
+#if defined(WEBRTC_WIN)
+#include <windows.h>
+#else
+#include <sched.h>
+#include <time.h>
+#endif
+
+namespace webrtc {
+
+void YieldCurrentThread() {
+ // TODO(bugs.webrtc.org/11634): use dedicated OS functionality instead of
+ // sleep for yielding.
+#if defined(WEBRTC_WIN)
+ ::Sleep(0);
+#elif defined(WEBRTC_MAC) && defined(RTC_USE_NATIVE_MUTEX_ON_MAC) && \
+ !RTC_USE_NATIVE_MUTEX_ON_MAC
+ sched_yield();
+#else
+ static const struct timespec ts_null = {0};
+ nanosleep(&ts_null, nullptr);
+#endif
+}
+
+} // namespace webrtc
diff --git a/rtc_base/synchronization/yield.h b/rtc_base/synchronization/yield.h
new file mode 100644
index 0000000000..d4f5f99f37
--- /dev/null
+++ b/rtc_base/synchronization/yield.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef RTC_BASE_SYNCHRONIZATION_YIELD_H_
+#define RTC_BASE_SYNCHRONIZATION_YIELD_H_
+
+namespace webrtc {
+
+// Request rescheduling of threads.
+void YieldCurrentThread();
+
+} // namespace webrtc
+
+#endif // RTC_BASE_SYNCHRONIZATION_YIELD_H_