Add a race-checking mechanism.
Permits CHECKing/DCHECKing that methods are being accessed in a thread-safe manner, even if they are not used by one single thread (thread pools such as VideoToolbox OK). BUG= R=danilchap@webrtc.org, tommi@webrtc.org Review URL: https://codereview.webrtc.org/2097403002 . Cr-Commit-Position: refs/heads/master@{#13358}
This commit is contained in:
@ -137,6 +137,8 @@ static_library("rtc_base_approved") {
|
|||||||
"platform_thread.cc",
|
"platform_thread.cc",
|
||||||
"platform_thread.h",
|
"platform_thread.h",
|
||||||
"platform_thread_types.h",
|
"platform_thread_types.h",
|
||||||
|
"race_checker.cc",
|
||||||
|
"race_checker.h",
|
||||||
"random.cc",
|
"random.cc",
|
||||||
"random.h",
|
"random.h",
|
||||||
"rate_statistics.cc",
|
"rate_statistics.cc",
|
||||||
|
|||||||
@ -68,6 +68,8 @@
|
|||||||
'platform_thread.cc',
|
'platform_thread.cc',
|
||||||
'platform_thread.h',
|
'platform_thread.h',
|
||||||
'platform_thread_types.h',
|
'platform_thread_types.h',
|
||||||
|
'race_checker.cc',
|
||||||
|
'race_checker.h',
|
||||||
'random.cc',
|
'random.cc',
|
||||||
'random.h',
|
'random.h',
|
||||||
'rate_statistics.cc',
|
'rate_statistics.cc',
|
||||||
|
|||||||
46
webrtc/base/race_checker.cc
Normal file
46
webrtc/base/race_checker.cc
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 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 "webrtc/base/race_checker.h"
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
RaceChecker::RaceChecker() {}
|
||||||
|
|
||||||
|
bool RaceChecker::Acquire() const {
|
||||||
|
const PlatformThreadRef current_thread = CurrentThreadRef();
|
||||||
|
// Set new accessing thread if this is a new use.
|
||||||
|
if (access_count_++ == 0)
|
||||||
|
accessing_thread_ = current_thread;
|
||||||
|
// If this is being used concurrently this check will fail for the second
|
||||||
|
// thread entering since it won't set the thread. Recursive use of checked
|
||||||
|
// methods are OK since the accessing thread remains the same.
|
||||||
|
const PlatformThreadRef accessing_thread = accessing_thread_;
|
||||||
|
return IsThreadRefEqual(accessing_thread, current_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaceChecker::Release() const {
|
||||||
|
--access_count_;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
RaceCheckerScope::RaceCheckerScope(const RaceChecker* race_checker)
|
||||||
|
: race_checker_(race_checker), race_check_ok_(race_checker->Acquire()) {}
|
||||||
|
|
||||||
|
bool RaceCheckerScope::RaceDetected() const {
|
||||||
|
return !race_check_ok_;
|
||||||
|
}
|
||||||
|
|
||||||
|
RaceCheckerScope::~RaceCheckerScope() {
|
||||||
|
race_checker_->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace rtc
|
||||||
78
webrtc/base/race_checker.h
Normal file
78
webrtc/base/race_checker.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 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 WEBRTC_BASE_RACE_CHECKER_H_
|
||||||
|
#define WEBRTC_BASE_RACE_CHECKER_H_
|
||||||
|
|
||||||
|
#include "webrtc/base/checks.h"
|
||||||
|
#include "webrtc/base/platform_thread.h"
|
||||||
|
#include "webrtc/base/thread_annotations.h"
|
||||||
|
|
||||||
|
namespace rtc {
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
class RaceCheckerScope;
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Best-effort race-checking implementation. This primitive uses no
|
||||||
|
// synchronization at all to be as-fast-as-possible in the non-racy case.
|
||||||
|
class LOCKABLE RaceChecker {
|
||||||
|
public:
|
||||||
|
friend class internal::RaceCheckerScope;
|
||||||
|
RaceChecker();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool Acquire() const EXCLUSIVE_LOCK_FUNCTION();
|
||||||
|
void Release() const UNLOCK_FUNCTION();
|
||||||
|
|
||||||
|
// Volatile to prevent code being optimized away in Acquire()/Release().
|
||||||
|
mutable volatile int access_count_ = 0;
|
||||||
|
mutable volatile PlatformThreadRef accessing_thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
class SCOPED_LOCKABLE RaceCheckerScope {
|
||||||
|
public:
|
||||||
|
explicit RaceCheckerScope(const RaceChecker* race_checker)
|
||||||
|
EXCLUSIVE_LOCK_FUNCTION(race_checker);
|
||||||
|
|
||||||
|
bool RaceDetected() const;
|
||||||
|
~RaceCheckerScope() UNLOCK_FUNCTION();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const RaceChecker* const race_checker_;
|
||||||
|
const bool race_check_ok_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SCOPED_LOCKABLE RaceCheckerScopeDoNothing {
|
||||||
|
public:
|
||||||
|
explicit RaceCheckerScopeDoNothing(const RaceChecker* race_checker)
|
||||||
|
EXCLUSIVE_LOCK_FUNCTION(race_checker) {}
|
||||||
|
|
||||||
|
~RaceCheckerScopeDoNothing() UNLOCK_FUNCTION() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace rtc
|
||||||
|
|
||||||
|
#define RTC_CHECK_RUNS_SERIALIZED(x) \
|
||||||
|
rtc::internal::RaceCheckerScope race_checker(x); \
|
||||||
|
RTC_CHECK(!race_checker.RaceDetected())
|
||||||
|
|
||||||
|
#if RTC_DCHECK_IS_ON
|
||||||
|
#define RTC_DCHECK_RUNS_SERIALIZED(x) \
|
||||||
|
rtc::internal::RaceCheckerScope race_checker(x); \
|
||||||
|
RTC_DCHECK(!race_checker.RaceDetected())
|
||||||
|
#else
|
||||||
|
#define RTC_DCHECK_RUNS_SERIALIZED(x) \
|
||||||
|
rtc::internal::RaceCheckerScopeDoNothing race_checker(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // WEBRTC_BASE_RACE_CHECKER_H_
|
||||||
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "webrtc/base/criticalsection.h"
|
#include "webrtc/base/criticalsection.h"
|
||||||
#include "webrtc/base/platform_thread.h"
|
#include "webrtc/base/platform_thread.h"
|
||||||
|
#include "webrtc/base/race_checker.h"
|
||||||
#include "webrtc/base/thread_annotations.h"
|
#include "webrtc/base/thread_annotations.h"
|
||||||
#include "webrtc/base/thread_checker.h"
|
#include "webrtc/base/thread_checker.h"
|
||||||
#include "webrtc/common_video/video_render_frames.h"
|
#include "webrtc/common_video/video_render_frames.h"
|
||||||
@ -42,6 +43,7 @@ class IncomingVideoStream : public rtc::VideoSinkInterface<VideoFrame> {
|
|||||||
|
|
||||||
rtc::ThreadChecker main_thread_checker_;
|
rtc::ThreadChecker main_thread_checker_;
|
||||||
rtc::ThreadChecker render_thread_checker_;
|
rtc::ThreadChecker render_thread_checker_;
|
||||||
|
rtc::RaceChecker decoder_race_checker_;
|
||||||
|
|
||||||
rtc::CriticalSection buffer_critsect_;
|
rtc::CriticalSection buffer_critsect_;
|
||||||
rtc::PlatformThread incoming_render_thread_;
|
rtc::PlatformThread incoming_render_thread_;
|
||||||
|
|||||||
@ -49,9 +49,7 @@ IncomingVideoStream::~IncomingVideoStream() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IncomingVideoStream::OnFrame(const VideoFrame& video_frame) {
|
void IncomingVideoStream::OnFrame(const VideoFrame& video_frame) {
|
||||||
// Most of the time we'll be on a decoder thread here, but when using
|
RTC_CHECK_RUNS_SERIALIZED(&decoder_race_checker_);
|
||||||
// VideoToolbox on iOS, we'll get called on a thread from a thread pool.
|
|
||||||
|
|
||||||
// Hand over or insert frame.
|
// Hand over or insert frame.
|
||||||
rtc::CritScope csB(&buffer_critsect_);
|
rtc::CritScope csB(&buffer_critsect_);
|
||||||
if (render_buffers_->AddFrame(video_frame) == 1) {
|
if (render_buffers_->AddFrame(video_frame) == 1) {
|
||||||
|
|||||||
@ -36,6 +36,7 @@ VCMGenericEncoder::VCMGenericEncoder(
|
|||||||
VCMGenericEncoder::~VCMGenericEncoder() {}
|
VCMGenericEncoder::~VCMGenericEncoder() {}
|
||||||
|
|
||||||
int32_t VCMGenericEncoder::Release() {
|
int32_t VCMGenericEncoder::Release() {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
TRACE_EVENT0("webrtc", "VCMGenericEncoder::Release");
|
TRACE_EVENT0("webrtc", "VCMGenericEncoder::Release");
|
||||||
return encoder_->Release();
|
return encoder_->Release();
|
||||||
}
|
}
|
||||||
@ -43,6 +44,7 @@ int32_t VCMGenericEncoder::Release() {
|
|||||||
int32_t VCMGenericEncoder::InitEncode(const VideoCodec* settings,
|
int32_t VCMGenericEncoder::InitEncode(const VideoCodec* settings,
|
||||||
int32_t number_of_cores,
|
int32_t number_of_cores,
|
||||||
size_t max_payload_size) {
|
size_t max_payload_size) {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
TRACE_EVENT0("webrtc", "VCMGenericEncoder::InitEncode");
|
TRACE_EVENT0("webrtc", "VCMGenericEncoder::InitEncode");
|
||||||
is_screenshare_ = settings->mode == VideoCodecMode::kScreensharing;
|
is_screenshare_ = settings->mode == VideoCodecMode::kScreensharing;
|
||||||
if (encoder_->InitEncode(settings, number_of_cores, max_payload_size) != 0) {
|
if (encoder_->InitEncode(settings, number_of_cores, max_payload_size) != 0) {
|
||||||
@ -58,6 +60,7 @@ int32_t VCMGenericEncoder::InitEncode(const VideoCodec* settings,
|
|||||||
int32_t VCMGenericEncoder::Encode(const VideoFrame& frame,
|
int32_t VCMGenericEncoder::Encode(const VideoFrame& frame,
|
||||||
const CodecSpecificInfo* codec_specific,
|
const CodecSpecificInfo* codec_specific,
|
||||||
const std::vector<FrameType>& frame_types) {
|
const std::vector<FrameType>& frame_types) {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
TRACE_EVENT1("webrtc", "VCMGenericEncoder::Encode", "timestamp",
|
TRACE_EVENT1("webrtc", "VCMGenericEncoder::Encode", "timestamp",
|
||||||
frame.timestamp());
|
frame.timestamp());
|
||||||
|
|
||||||
@ -76,10 +79,12 @@ int32_t VCMGenericEncoder::Encode(const VideoFrame& frame,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char* VCMGenericEncoder::ImplementationName() const {
|
const char* VCMGenericEncoder::ImplementationName() const {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
return encoder_->ImplementationName();
|
return encoder_->ImplementationName();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VCMGenericEncoder::SetEncoderParameters(const EncoderParameters& params) {
|
void VCMGenericEncoder::SetEncoderParameters(const EncoderParameters& params) {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
bool channel_parameters_have_changed;
|
bool channel_parameters_have_changed;
|
||||||
bool rates_have_changed;
|
bool rates_have_changed;
|
||||||
{
|
{
|
||||||
@ -110,11 +115,13 @@ EncoderParameters VCMGenericEncoder::GetEncoderParameters() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int32_t VCMGenericEncoder::SetPeriodicKeyFrames(bool enable) {
|
int32_t VCMGenericEncoder::SetPeriodicKeyFrames(bool enable) {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
return encoder_->SetPeriodicKeyFrames(enable);
|
return encoder_->SetPeriodicKeyFrames(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t VCMGenericEncoder::RequestFrame(
|
int32_t VCMGenericEncoder::RequestFrame(
|
||||||
const std::vector<FrameType>& frame_types) {
|
const std::vector<FrameType>& frame_types) {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
VideoFrame image;
|
VideoFrame image;
|
||||||
return encoder_->Encode(image, NULL, &frame_types);
|
return encoder_->Encode(image, NULL, &frame_types);
|
||||||
}
|
}
|
||||||
@ -124,10 +131,12 @@ bool VCMGenericEncoder::InternalSource() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VCMGenericEncoder::OnDroppedFrame() {
|
void VCMGenericEncoder::OnDroppedFrame() {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
encoder_->OnDroppedFrame();
|
encoder_->OnDroppedFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VCMGenericEncoder::SupportsNativeHandle() const {
|
bool VCMGenericEncoder::SupportsNativeHandle() const {
|
||||||
|
RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
|
||||||
return encoder_->SupportsNativeHandle();
|
return encoder_->SupportsNativeHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
#include "webrtc/modules/video_coding/include/video_coding_defines.h"
|
#include "webrtc/modules/video_coding/include/video_coding_defines.h"
|
||||||
|
|
||||||
#include "webrtc/base/criticalsection.h"
|
#include "webrtc/base/criticalsection.h"
|
||||||
|
#include "webrtc/base/race_checker.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
class CriticalSectionWrapper;
|
class CriticalSectionWrapper;
|
||||||
@ -82,7 +83,9 @@ class VCMGenericEncoder {
|
|||||||
bool SupportsNativeHandle() const;
|
bool SupportsNativeHandle() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VideoEncoder* const encoder_;
|
rtc::RaceChecker race_checker_;
|
||||||
|
|
||||||
|
VideoEncoder* const encoder_ GUARDED_BY(race_checker_);
|
||||||
VideoEncoderRateObserver* const rate_observer_;
|
VideoEncoderRateObserver* const rate_observer_;
|
||||||
VCMEncodedFrameCallback* const vcm_encoded_frame_callback_;
|
VCMEncodedFrameCallback* const vcm_encoded_frame_callback_;
|
||||||
const bool internal_source_;
|
const bool internal_source_;
|
||||||
|
|||||||
Reference in New Issue
Block a user