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:
Markus Handell
2020-06-04 00:41:20 +02:00
committed by Commit Bot
parent e917379c5b
commit f70fbc8411
24 changed files with 779 additions and 44 deletions

View File

@ -580,6 +580,14 @@ if (rtc_include_tests) {
} }
} }
rtc_test("benchmarks") {
testonly = true
deps = [
"rtc_base/synchronization:mutex_benchmark",
"test:benchmark_main",
]
}
# This runs tests that must run in real time and therefore can take some # This runs tests that must run in real time and therefore can take some
# time to execute. They are in a separate executable to avoid making the # time to execute. They are in a separate executable to avoid making the
# regular unittest suite too slow to run frequently. # regular unittest suite too slow to run frequently.

4
DEPS
View File

@ -146,6 +146,9 @@ deps = {
'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@62fea391fa9993f8c1d206a50080d690178ce518', 'https://chromium.googlesource.com/chromium/src/third_party/freetype2.git@62fea391fa9993f8c1d206a50080d690178ce518',
'src/third_party/harfbuzz-ng/src': 'src/third_party/harfbuzz-ng/src':
'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@e3af529e511ca492284cdd9f4584666b88a9e00f', 'https://chromium.googlesource.com/external/github.com/harfbuzz/harfbuzz.git@e3af529e511ca492284cdd9f4584666b88a9e00f',
'src/third_party/google_benchmark/src': {
'url': 'https://chromium.googlesource.com/external/github.com/google/benchmark.git@6746c65bcfa49110bfe6642b8a47735637817be4',
},
# WebRTC-only dependency (not present in Chromium). # WebRTC-only dependency (not present in Chromium).
'src/third_party/gtest-parallel': 'src/third_party/gtest-parallel':
'https://chromium.googlesource.com/external/github.com/google/gtest-parallel@df0b4e476f98516cea7d593e5dbb0fca44f6ee7f', 'https://chromium.googlesource.com/external/github.com/google/gtest-parallel@df0b4e476f98516cea7d593e5dbb0fca44f6ee7f',
@ -3224,6 +3227,7 @@ include_rules = [
"+absl/algorithm/container.h", "+absl/algorithm/container.h",
"+absl/base/attributes.h", "+absl/base/attributes.h",
"+absl/base/config.h", "+absl/base/config.h",
"+absl/base/const_init.h",
"+absl/base/macros.h", "+absl/base/macros.h",
"+absl/container/inlined_vector.h", "+absl/container/inlined_vector.h",
"+absl/memory/memory.h", "+absl/memory/memory.h",

View File

@ -23,9 +23,11 @@ adds the first use.
* `absl::variant` and related stuff from `absl/types/variant.h`. * `absl::variant` and related stuff from `absl/types/variant.h`.
* The functions in `absl/algorithm/algorithm.h` and * The functions in `absl/algorithm/algorithm.h` and
`absl/algorithm/container.h`. `absl/algorithm/container.h`.
* `absl/base/const_init.h` for mutex initialization.
* The macros in `absl/base/attributes.h`, `absl/base/config.h` and * The macros in `absl/base/attributes.h`, `absl/base/config.h` and
`absl/base/macros.h`. `absl/base/macros.h`.
## **Disallowed** ## **Disallowed**
### `absl::make_unique` ### `absl::make_unique`
@ -34,7 +36,7 @@ adds the first use.
### `absl::Mutex` ### `absl::Mutex`
*Use `rtc::CriticalSection` instead.* *Use `webrtc::Mutex` instead.*
Chromium has a ban on new static initializers, and `absl::Mutex` uses Chromium has a ban on new static initializers, and `absl::Mutex` uses
one. To make `absl::Mutex` available, we would need to nicely ask the one. To make `absl::Mutex` available, we would need to nicely ask the

View File

@ -16,6 +16,9 @@ linux_use_bundled_binutils_override = true
# only needed to support both WebRTC standalone and Chromium builds. # only needed to support both WebRTC standalone and Chromium builds.
build_with_chromium = false build_with_chromium = false
# WebRTC checks out google_benchmark by default since it is always used.
checkout_google_benchmark = true
# Use our own suppressions files. # Use our own suppressions files.
asan_suppressions_file = "//build/sanitizers/asan_suppressions.cc" asan_suppressions_file = "//build/sanitizers/asan_suppressions.cc"
lsan_suppressions_file = "//tools_webrtc/sanitizers/lsan_suppressions_webrtc.cc" lsan_suppressions_file = "//tools_webrtc/sanitizers/lsan_suppressions_webrtc.cc"

View File

@ -192,6 +192,7 @@ rtc_library("video_coding") {
"../../rtc_base/experiments:min_video_bitrate_experiment", "../../rtc_base/experiments:min_video_bitrate_experiment",
"../../rtc_base/experiments:rate_control_settings", "../../rtc_base/experiments:rate_control_settings",
"../../rtc_base/experiments:rtt_mult_experiment", "../../rtc_base/experiments:rtt_mult_experiment",
"../../rtc_base/synchronization:mutex",
"../../rtc_base/synchronization:sequence_checker", "../../rtc_base/synchronization:sequence_checker",
"../../rtc_base/task_utils:repeating_task", "../../rtc_base/task_utils:repeating_task",
"../../rtc_base/third_party/base64", "../../rtc_base/third_party/base64",

View File

@ -78,7 +78,7 @@ PacketBuffer::~PacketBuffer() {
PacketBuffer::InsertResult PacketBuffer::InsertPacket( PacketBuffer::InsertResult PacketBuffer::InsertPacket(
std::unique_ptr<PacketBuffer::Packet> packet) { std::unique_ptr<PacketBuffer::Packet> packet) {
PacketBuffer::InsertResult result; PacketBuffer::InsertResult result;
rtc::CritScope lock(&crit_); MutexLock lock(&mutex_);
uint16_t seq_num = packet->seq_num; uint16_t seq_num = packet->seq_num;
size_t index = seq_num % buffer_.size(); size_t index = seq_num % buffer_.size();
@ -136,7 +136,7 @@ PacketBuffer::InsertResult PacketBuffer::InsertPacket(
} }
void PacketBuffer::ClearTo(uint16_t seq_num) { void PacketBuffer::ClearTo(uint16_t seq_num) {
rtc::CritScope lock(&crit_); MutexLock lock(&mutex_);
// We have already cleared past this sequence number, no need to do anything. // We have already cleared past this sequence number, no need to do anything.
if (is_cleared_to_first_seq_num_ && if (is_cleared_to_first_seq_num_ &&
AheadOf<uint16_t>(first_seq_num_, seq_num)) { AheadOf<uint16_t>(first_seq_num_, seq_num)) {
@ -173,25 +173,25 @@ void PacketBuffer::ClearTo(uint16_t seq_num) {
} }
void PacketBuffer::Clear() { void PacketBuffer::Clear() {
rtc::CritScope lock(&crit_); MutexLock lock(&mutex_);
ClearInternal(); ClearInternal();
} }
PacketBuffer::InsertResult PacketBuffer::InsertPadding(uint16_t seq_num) { PacketBuffer::InsertResult PacketBuffer::InsertPadding(uint16_t seq_num) {
PacketBuffer::InsertResult result; PacketBuffer::InsertResult result;
rtc::CritScope lock(&crit_); MutexLock lock(&mutex_);
UpdateMissingPackets(seq_num); UpdateMissingPackets(seq_num);
result.packets = FindFrames(static_cast<uint16_t>(seq_num + 1)); result.packets = FindFrames(static_cast<uint16_t>(seq_num + 1));
return result; return result;
} }
absl::optional<int64_t> PacketBuffer::LastReceivedPacketMs() const { absl::optional<int64_t> PacketBuffer::LastReceivedPacketMs() const {
rtc::CritScope lock(&crit_); MutexLock lock(&mutex_);
return last_received_packet_ms_; return last_received_packet_ms_;
} }
absl::optional<int64_t> PacketBuffer::LastReceivedKeyframePacketMs() const { absl::optional<int64_t> PacketBuffer::LastReceivedKeyframePacketMs() const {
rtc::CritScope lock(&crit_); MutexLock lock(&mutex_);
return last_received_keyframe_packet_ms_; return last_received_keyframe_packet_ms_;
} }

View File

@ -22,8 +22,8 @@
#include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "modules/rtp_rtcp/source/rtp_video_header.h" #include "modules/rtp_rtcp/source/rtp_video_header.h"
#include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/critical_section.h"
#include "rtc_base/numerics/sequence_number_util.h" #include "rtc_base/numerics/sequence_number_util.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h" #include "rtc_base/thread_annotations.h"
#include "system_wrappers/include/clock.h" #include "system_wrappers/include/clock.h"
@ -83,67 +83,67 @@ class PacketBuffer {
~PacketBuffer(); ~PacketBuffer();
InsertResult InsertPacket(std::unique_ptr<Packet> packet) ABSL_MUST_USE_RESULT InsertResult InsertPacket(std::unique_ptr<Packet> packet) ABSL_MUST_USE_RESULT
RTC_LOCKS_EXCLUDED(crit_); RTC_LOCKS_EXCLUDED(mutex_);
InsertResult InsertPadding(uint16_t seq_num) ABSL_MUST_USE_RESULT InsertResult InsertPadding(uint16_t seq_num) ABSL_MUST_USE_RESULT
RTC_LOCKS_EXCLUDED(crit_); RTC_LOCKS_EXCLUDED(mutex_);
void ClearTo(uint16_t seq_num) RTC_LOCKS_EXCLUDED(crit_); void ClearTo(uint16_t seq_num) RTC_LOCKS_EXCLUDED(mutex_);
void Clear() RTC_LOCKS_EXCLUDED(crit_); void Clear() RTC_LOCKS_EXCLUDED(mutex_);
// Timestamp (not RTP timestamp) of the last received packet/keyframe packet. // Timestamp (not RTP timestamp) of the last received packet/keyframe packet.
absl::optional<int64_t> LastReceivedPacketMs() const absl::optional<int64_t> LastReceivedPacketMs() const
RTC_LOCKS_EXCLUDED(crit_); RTC_LOCKS_EXCLUDED(mutex_);
absl::optional<int64_t> LastReceivedKeyframePacketMs() const absl::optional<int64_t> LastReceivedKeyframePacketMs() const
RTC_LOCKS_EXCLUDED(crit_); RTC_LOCKS_EXCLUDED(mutex_);
private: private:
Clock* const clock_; Clock* const clock_;
// Clears with |crit_| taken. // Clears with |mutex_| taken.
void ClearInternal() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); void ClearInternal() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Tries to expand the buffer. // Tries to expand the buffer.
bool ExpandBufferSize() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); bool ExpandBufferSize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Test if all previous packets has arrived for the given sequence number. // Test if all previous packets has arrived for the given sequence number.
bool PotentialNewFrame(uint16_t seq_num) const bool PotentialNewFrame(uint16_t seq_num) const
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Test if all packets of a frame has arrived, and if so, returns packets to // Test if all packets of a frame has arrived, and if so, returns packets to
// create frames. // create frames.
std::vector<std::unique_ptr<Packet>> FindFrames(uint16_t seq_num) std::vector<std::unique_ptr<Packet>> FindFrames(uint16_t seq_num)
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void UpdateMissingPackets(uint16_t seq_num) void UpdateMissingPackets(uint16_t seq_num)
RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_); RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
rtc::CriticalSection crit_; mutable Mutex mutex_;
// buffer_.size() and max_size_ must always be a power of two. // buffer_.size() and max_size_ must always be a power of two.
const size_t max_size_; const size_t max_size_;
// The fist sequence number currently in the buffer. // The fist sequence number currently in the buffer.
uint16_t first_seq_num_ RTC_GUARDED_BY(crit_); uint16_t first_seq_num_ RTC_GUARDED_BY(mutex_);
// If the packet buffer has received its first packet. // If the packet buffer has received its first packet.
bool first_packet_received_ RTC_GUARDED_BY(crit_); bool first_packet_received_ RTC_GUARDED_BY(mutex_);
// If the buffer is cleared to |first_seq_num_|. // If the buffer is cleared to |first_seq_num_|.
bool is_cleared_to_first_seq_num_ RTC_GUARDED_BY(crit_); bool is_cleared_to_first_seq_num_ RTC_GUARDED_BY(mutex_);
// Buffer that holds the the inserted packets and information needed to // Buffer that holds the the inserted packets and information needed to
// determine continuity between them. // determine continuity between them.
std::vector<std::unique_ptr<Packet>> buffer_ RTC_GUARDED_BY(crit_); std::vector<std::unique_ptr<Packet>> buffer_ RTC_GUARDED_BY(mutex_);
// Timestamp of the last received packet/keyframe packet. // Timestamp of the last received packet/keyframe packet.
absl::optional<int64_t> last_received_packet_ms_ RTC_GUARDED_BY(crit_); absl::optional<int64_t> last_received_packet_ms_ RTC_GUARDED_BY(mutex_);
absl::optional<int64_t> last_received_keyframe_packet_ms_ absl::optional<int64_t> last_received_keyframe_packet_ms_
RTC_GUARDED_BY(crit_); RTC_GUARDED_BY(mutex_);
absl::optional<uint32_t> last_received_keyframe_rtp_timestamp_ absl::optional<uint32_t> last_received_keyframe_rtp_timestamp_
RTC_GUARDED_BY(crit_); RTC_GUARDED_BY(mutex_);
absl::optional<uint16_t> newest_inserted_seq_num_ RTC_GUARDED_BY(crit_); absl::optional<uint16_t> newest_inserted_seq_num_ RTC_GUARDED_BY(mutex_);
std::set<uint16_t, DescendingSeqNumComp<uint16_t>> missing_packets_ std::set<uint16_t, DescendingSeqNumComp<uint16_t>> missing_packets_
RTC_GUARDED_BY(crit_); RTC_GUARDED_BY(mutex_);
// Indicates if we should require SPS, PPS, and IDR for a particular // Indicates if we should require SPS, PPS, and IDR for a particular
// RTP timestamp to treat the corresponding frame as a keyframe. // RTP timestamp to treat the corresponding frame as a keyframe.

View File

@ -177,6 +177,7 @@ rtc_library("criticalsection") {
":checks", ":checks",
":macromagic", ":macromagic",
":platform_thread_types", ":platform_thread_types",
"synchronization:yield",
"system:rtc_export", "system:rtc_export",
"system:unused", "system:unused",
] ]

View File

@ -1,8 +1,8 @@
include_rules = [ include_rules = [
"+base/third_party/libevent", "+base/third_party/libevent",
"+json", "+json",
"+third_party/jsoncpp",
"+system_wrappers", "+system_wrappers",
"+third_party/jsoncpp",
] ]
specific_include_rules = { specific_include_rules = {

View File

@ -15,6 +15,7 @@
#include "rtc_base/atomic_ops.h" #include "rtc_base/atomic_ops.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/platform_thread_types.h" #include "rtc_base/platform_thread_types.h"
#include "rtc_base/synchronization/yield.h"
#include "rtc_base/system/unused.h" #include "rtc_base/system/unused.h"
// TODO(tommi): Split this file up to per-platform implementation files. // TODO(tommi): Split this file up to per-platform implementation files.
@ -217,19 +218,8 @@ CritScope::~CritScope() {
} }
void GlobalLock::Lock() { 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)) { while (AtomicOps::CompareAndSwap(&lock_acquired_, 0, 1)) {
#if defined(WEBRTC_WIN) webrtc::YieldCurrentThread();
::Sleep(0);
#elif defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
sched_yield();
#else
nanosleep(&ts_null, nullptr);
#endif
} }
} }

View File

@ -11,6 +11,33 @@ if (is_android) {
import("//build/config/android/config.gni") import("//build/config/android/config.gni")
import("//build/config/android/rules.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") { rtc_library("rw_lock_wrapper") {
public = [ "rw_lock_wrapper.h" ] public = [ "rw_lock_wrapper.h" ]
@ -60,11 +87,29 @@ rtc_library("yield_policy") {
if (rtc_include_tests) { if (rtc_include_tests) {
rtc_library("synchronization_unittests") { rtc_library("synchronization_unittests") {
testonly = true testonly = true
sources = [ "yield_policy_unittest.cc" ] sources = [
"mutex_unittest.cc",
"yield_policy_unittest.cc",
]
deps = [ deps = [
":mutex",
":yield",
":yield_policy", ":yield_policy",
"..:checks",
"..:macromagic",
"..:rtc_base",
"..:rtc_event", "..:rtc_event",
"../../test:test_support", "../../test:test_support",
"//third_party/google_benchmark",
]
}
rtc_library("mutex_benchmark") {
testonly = true
sources = [ "mutex_benchmark.cc" ]
deps = [
":mutex",
"//third_party/google_benchmark",
] ]
} }

View File

@ -0,0 +1,11 @@
specific_include_rules = {
"mutex_abseil\.h": [
"+absl/synchronization"
],
".*_benchmark\.cc": [
"+benchmark",
],
".*_unittest\.cc": [
"+benchmark",
]
}

View 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

View 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_

View 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_

View 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
*/

View 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_

View 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_

View 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

View 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

View 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_

View File

@ -414,6 +414,13 @@ if (rtc_include_tests) {
] ]
} }
rtc_library("benchmark_main") {
testonly = true
sources = [ "benchmark_main.cc" ]
deps = [ "//third_party/google_benchmark" ]
}
rtc_library("test_support_test_artifacts") { rtc_library("test_support_test_artifacts") {
testonly = true testonly = true
sources = [ sources = [

View File

@ -72,5 +72,8 @@ specific_include_rules = {
], ],
".*test_video_capturer_video_track_source.h": [ ".*test_video_capturer_video_track_source.h": [
"+pc", "+pc",
],
"benchmark_main\.cc": [
"+benchmark",
] ]
} }

17
test/benchmark_main.cc Normal file
View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 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"
int main(int argc, char* argv[]) {
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
return 0;
}