diff --git a/webrtc/base/BUILD.gn b/webrtc/base/BUILD.gn index 94c4d81e3d..cd4bc1fe43 100644 --- a/webrtc/base/BUILD.gn +++ b/webrtc/base/BUILD.gn @@ -137,6 +137,8 @@ static_library("rtc_base_approved") { "platform_thread.cc", "platform_thread.h", "platform_thread_types.h", + "race_checker.cc", + "race_checker.h", "random.cc", "random.h", "rate_statistics.cc", diff --git a/webrtc/base/base.gyp b/webrtc/base/base.gyp index 9fdb304ef1..93be3bc706 100644 --- a/webrtc/base/base.gyp +++ b/webrtc/base/base.gyp @@ -68,6 +68,8 @@ 'platform_thread.cc', 'platform_thread.h', 'platform_thread_types.h', + 'race_checker.cc', + 'race_checker.h', 'random.cc', 'random.h', 'rate_statistics.cc', diff --git a/webrtc/base/race_checker.cc b/webrtc/base/race_checker.cc new file mode 100644 index 0000000000..9f0946ca76 --- /dev/null +++ b/webrtc/base/race_checker.cc @@ -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 diff --git a/webrtc/base/race_checker.h b/webrtc/base/race_checker.h new file mode 100644 index 0000000000..a6ba771f90 --- /dev/null +++ b/webrtc/base/race_checker.h @@ -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_ diff --git a/webrtc/common_video/include/incoming_video_stream.h b/webrtc/common_video/include/incoming_video_stream.h index a23ac05e9d..250bed8dbf 100644 --- a/webrtc/common_video/include/incoming_video_stream.h +++ b/webrtc/common_video/include/incoming_video_stream.h @@ -15,6 +15,7 @@ #include "webrtc/base/criticalsection.h" #include "webrtc/base/platform_thread.h" +#include "webrtc/base/race_checker.h" #include "webrtc/base/thread_annotations.h" #include "webrtc/base/thread_checker.h" #include "webrtc/common_video/video_render_frames.h" @@ -42,6 +43,7 @@ class IncomingVideoStream : public rtc::VideoSinkInterface { rtc::ThreadChecker main_thread_checker_; rtc::ThreadChecker render_thread_checker_; + rtc::RaceChecker decoder_race_checker_; rtc::CriticalSection buffer_critsect_; rtc::PlatformThread incoming_render_thread_; diff --git a/webrtc/common_video/incoming_video_stream.cc b/webrtc/common_video/incoming_video_stream.cc index 2ab3e84453..ed7b9ea29d 100644 --- a/webrtc/common_video/incoming_video_stream.cc +++ b/webrtc/common_video/incoming_video_stream.cc @@ -49,9 +49,7 @@ IncomingVideoStream::~IncomingVideoStream() { } void IncomingVideoStream::OnFrame(const VideoFrame& video_frame) { - // Most of the time we'll be on a decoder thread here, but when using - // VideoToolbox on iOS, we'll get called on a thread from a thread pool. - + RTC_CHECK_RUNS_SERIALIZED(&decoder_race_checker_); // Hand over or insert frame. rtc::CritScope csB(&buffer_critsect_); if (render_buffers_->AddFrame(video_frame) == 1) { diff --git a/webrtc/modules/video_coding/generic_encoder.cc b/webrtc/modules/video_coding/generic_encoder.cc index 176a14f078..0cc3d89a85 100644 --- a/webrtc/modules/video_coding/generic_encoder.cc +++ b/webrtc/modules/video_coding/generic_encoder.cc @@ -36,6 +36,7 @@ VCMGenericEncoder::VCMGenericEncoder( VCMGenericEncoder::~VCMGenericEncoder() {} int32_t VCMGenericEncoder::Release() { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); TRACE_EVENT0("webrtc", "VCMGenericEncoder::Release"); return encoder_->Release(); } @@ -43,6 +44,7 @@ int32_t VCMGenericEncoder::Release() { int32_t VCMGenericEncoder::InitEncode(const VideoCodec* settings, int32_t number_of_cores, size_t max_payload_size) { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); TRACE_EVENT0("webrtc", "VCMGenericEncoder::InitEncode"); is_screenshare_ = settings->mode == VideoCodecMode::kScreensharing; 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, const CodecSpecificInfo* codec_specific, const std::vector& frame_types) { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); TRACE_EVENT1("webrtc", "VCMGenericEncoder::Encode", "timestamp", frame.timestamp()); @@ -76,10 +79,12 @@ int32_t VCMGenericEncoder::Encode(const VideoFrame& frame, } const char* VCMGenericEncoder::ImplementationName() const { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); return encoder_->ImplementationName(); } void VCMGenericEncoder::SetEncoderParameters(const EncoderParameters& params) { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); bool channel_parameters_have_changed; bool rates_have_changed; { @@ -110,11 +115,13 @@ EncoderParameters VCMGenericEncoder::GetEncoderParameters() const { } int32_t VCMGenericEncoder::SetPeriodicKeyFrames(bool enable) { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); return encoder_->SetPeriodicKeyFrames(enable); } int32_t VCMGenericEncoder::RequestFrame( const std::vector& frame_types) { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); VideoFrame image; return encoder_->Encode(image, NULL, &frame_types); } @@ -124,10 +131,12 @@ bool VCMGenericEncoder::InternalSource() const { } void VCMGenericEncoder::OnDroppedFrame() { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); encoder_->OnDroppedFrame(); } bool VCMGenericEncoder::SupportsNativeHandle() const { + RTC_DCHECK_RUNS_SERIALIZED(&race_checker_); return encoder_->SupportsNativeHandle(); } diff --git a/webrtc/modules/video_coding/generic_encoder.h b/webrtc/modules/video_coding/generic_encoder.h index 469f04ded4..0493d319aa 100644 --- a/webrtc/modules/video_coding/generic_encoder.h +++ b/webrtc/modules/video_coding/generic_encoder.h @@ -18,6 +18,7 @@ #include "webrtc/modules/video_coding/include/video_coding_defines.h" #include "webrtc/base/criticalsection.h" +#include "webrtc/base/race_checker.h" namespace webrtc { class CriticalSectionWrapper; @@ -82,7 +83,9 @@ class VCMGenericEncoder { bool SupportsNativeHandle() const; private: - VideoEncoder* const encoder_; + rtc::RaceChecker race_checker_; + + VideoEncoder* const encoder_ GUARDED_BY(race_checker_); VideoEncoderRateObserver* const rate_observer_; VCMEncodedFrameCallback* const vcm_encoded_frame_callback_; const bool internal_source_;