diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn index 8ca719d8b6..c5531bf294 100644 --- a/rtc_base/BUILD.gn +++ b/rtc_base/BUILD.gn @@ -258,6 +258,7 @@ rtc_source_set("rtc_event") { "../../webrtc_overrides/rtc_base/event.h", ] } else { + deps += [ "synchronization:yield_policy" ] sources = [ "event.cc", "event.h", @@ -1409,6 +1410,7 @@ if (rtc_include_tests) { "../test:field_trial", "../test:fileutils", "../test:test_support", + "synchronization:synchronization_unittests", "third_party/sigslot", "//third_party/abseil-cpp/absl/memory", "//third_party/abseil-cpp/absl/types:optional", diff --git a/rtc_base/event.cc b/rtc_base/event.cc index 42c22a29cc..71dca4980d 100644 --- a/rtc_base/event.cc +++ b/rtc_base/event.cc @@ -21,6 +21,7 @@ #endif #include "rtc_base/checks.h" +#include "rtc_base/synchronization/yield_policy.h" namespace rtc { @@ -48,6 +49,7 @@ void Event::Reset() { } bool Event::Wait(int milliseconds) { + ScopedYieldPolicy::YieldExecution(); DWORD ms = (milliseconds == kForever) ? INFINITE : milliseconds; return (WaitForSingleObject(event_handle_, ms) == WAIT_OBJECT_0); } @@ -102,6 +104,8 @@ void Event::Reset() { } bool Event::Wait(int milliseconds) { + ScopedYieldPolicy::YieldExecution(); + int error = 0; struct timespec ts; diff --git a/rtc_base/synchronization/BUILD.gn b/rtc_base/synchronization/BUILD.gn index 447be38b8a..05d36f1099 100644 --- a/rtc_base/synchronization/BUILD.gn +++ b/rtc_base/synchronization/BUILD.gn @@ -35,3 +35,27 @@ rtc_source_set("rw_lock_wrapper") { ] } } + +rtc_source_set("yield_policy") { + sources = [ + "yield_policy.cc", + "yield_policy.h", + ] + deps = [ + "//third_party/abseil-cpp/absl/base:core_headers", + ] +} + +if (rtc_include_tests) { + rtc_source_set("synchronization_unittests") { + testonly = true + sources = [ + "yield_policy_unittest.cc", + ] + deps = [ + ":yield_policy", + "..:rtc_event", + "../../test:test_support", + ] + } +} diff --git a/rtc_base/synchronization/yield_policy.cc b/rtc_base/synchronization/yield_policy.cc new file mode 100644 index 0000000000..56159159c2 --- /dev/null +++ b/rtc_base/synchronization/yield_policy.cc @@ -0,0 +1,32 @@ +/* + * Copyright 2019 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_policy.h" + +#include "absl/base/attributes.h" + +namespace rtc { +namespace { +ABSL_CONST_INIT thread_local YieldInterface* current_yield_policy = nullptr; +} + +ScopedYieldPolicy::ScopedYieldPolicy(YieldInterface* policy) + : previous_(current_yield_policy) { + current_yield_policy = policy; +} + +ScopedYieldPolicy::~ScopedYieldPolicy() { + current_yield_policy = previous_; +} + +void ScopedYieldPolicy::YieldExecution() { + if (current_yield_policy) + current_yield_policy->YieldExecution(); +} +} // namespace rtc diff --git a/rtc_base/synchronization/yield_policy.h b/rtc_base/synchronization/yield_policy.h new file mode 100644 index 0000000000..8146930666 --- /dev/null +++ b/rtc_base/synchronization/yield_policy.h @@ -0,0 +1,36 @@ +/* + * Copyright 2019 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_POLICY_H_ +#define RTC_BASE_SYNCHRONIZATION_YIELD_POLICY_H_ + +namespace rtc { +class YieldInterface { + public: + virtual ~YieldInterface() = default; + virtual void YieldExecution() = 0; +}; + +// Sets the current thread-local yield policy while it's in scope and reverts +// to the previous policy when it leaves the scope. +class ScopedYieldPolicy final { + public: + explicit ScopedYieldPolicy(YieldInterface* policy); + ~ScopedYieldPolicy(); + // Will yield as specified by the currently active thread-local yield policy + // (which by default is a no-op). + static void YieldExecution(); + + private: + YieldInterface* const previous_; +}; + +} // namespace rtc + +#endif // RTC_BASE_SYNCHRONIZATION_YIELD_POLICY_H_ diff --git a/rtc_base/synchronization/yield_policy_unittest.cc b/rtc_base/synchronization/yield_policy_unittest.cc new file mode 100644 index 0000000000..d7e352b22c --- /dev/null +++ b/rtc_base/synchronization/yield_policy_unittest.cc @@ -0,0 +1,67 @@ +/* + * Copyright 2019 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 // Not allowed in production per Chromium style guide. + +#include "rtc_base/event.h" +#include "rtc_base/synchronization/yield_policy.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace rtc { +namespace { +class MockYieldHandler : public YieldInterface { + public: + MOCK_METHOD0(YieldExecution, void()); +}; +} // namespace +TEST(YieldPolicyTest, HandlerReceivesYieldSignalWhenSet) { + testing::StrictMock handler; + { + Event event; + EXPECT_CALL(handler, YieldExecution()).Times(1); + ScopedYieldPolicy policy(&handler); + event.Set(); + event.Wait(Event::kForever); + } + { + Event event; + EXPECT_CALL(handler, YieldExecution()).Times(0); + event.Set(); + event.Wait(Event::kForever); + } +} + +TEST(YieldPolicyTest, IsThreadLocal) { + Event events[3]; + std::thread other_thread([&]() { + testing::StrictMock local_handler; + // The local handler is never called as we never Wait on this thread. + EXPECT_CALL(local_handler, YieldExecution()).Times(0); + ScopedYieldPolicy policy(&local_handler); + events[0].Set(); + events[1].Set(); + events[2].Set(); + }); + + // Waiting until the other thread has entered the scoped policy. + events[0].Wait(Event::kForever); + // Wait on this thread should not trigger the handler of that policy as it's + // thread local. + events[1].Wait(Event::kForever); + + // We can set a policy that's active on this thread independently. + testing::StrictMock main_handler; + EXPECT_CALL(main_handler, YieldExecution()).Times(1); + ScopedYieldPolicy policy(&main_handler); + events[2].Wait(Event::kForever); + other_thread.join(); +} +} // namespace rtc