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
8
BUILD.gn
8
BUILD.gn
@ -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
4
DEPS
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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",
|
||||||
|
@ -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_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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",
|
||||||
]
|
]
|
||||||
|
@ -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 = {
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_
|
@ -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 = [
|
||||||
|
@ -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
17
test/benchmark_main.cc
Normal 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;
|
||||||
|
}
|
Reference in New Issue
Block a user