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}
This commit is contained in:
committed by
Commit Bot
parent
e917379c5b
commit
f70fbc8411
@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
11
rtc_base/synchronization/DEPS
Normal file
11
rtc_base/synchronization/DEPS
Normal file
@ -0,0 +1,11 @@
|
||||
specific_include_rules = {
|
||||
"mutex_abseil\.h": [
|
||||
"+absl/synchronization"
|
||||
],
|
||||
".*_benchmark\.cc": [
|
||||
"+benchmark",
|
||||
],
|
||||
".*_unittest\.cc": [
|
||||
"+benchmark",
|
||||
]
|
||||
}
|
||||
39
rtc_base/synchronization/mutex.cc
Normal file
39
rtc_base/synchronization/mutex.cc
Normal file
@ -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
|
||||
103
rtc_base/synchronization/mutex.h
Normal file
103
rtc_base/synchronization/mutex.h
Normal file
@ -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_
|
||||
37
rtc_base/synchronization/mutex_abseil.h
Normal file
37
rtc_base/synchronization/mutex_abseil.h
Normal file
@ -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_
|
||||
95
rtc_base/synchronization/mutex_benchmark.cc
Normal file
95
rtc_base/synchronization/mutex_benchmark.cc
Normal file
@ -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
|
||||
|
||||
*/
|
||||
54
rtc_base/synchronization/mutex_critical_section.h
Normal file
54
rtc_base/synchronization/mutex_critical_section.h
Normal file
@ -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_
|
||||
53
rtc_base/synchronization/mutex_pthread.h
Normal file
53
rtc_base/synchronization/mutex_pthread.h
Normal file
@ -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_
|
||||
206
rtc_base/synchronization/mutex_unittest.cc
Normal file
206
rtc_base/synchronization/mutex_unittest.cc
Normal file
@ -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
|
||||
36
rtc_base/synchronization/yield.cc
Normal file
36
rtc_base/synchronization/yield.cc
Normal file
@ -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
|
||||
20
rtc_base/synchronization/yield.h
Normal file
20
rtc_base/synchronization/yield.h
Normal file
@ -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_
|
||||
Reference in New Issue
Block a user