Adds ability to tell Event::Wait to yield.
This will be used by simulated time controller to allow processing other
tasks while waiting on an Event. This makes posting of blocking tasks
possible.
Bug: webrtc:10365
Change-Id: Ic3fb156d545eed2c036939121b89295433176e26
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/128121
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27214}
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 8ca719d..c5531bf2 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -258,6 +258,7 @@
"../../webrtc_overrides/rtc_base/event.h",
]
} else {
+ deps += [ "synchronization:yield_policy" ]
sources = [
"event.cc",
"event.h",
@@ -1409,6 +1410,7 @@
"../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 42c22a2..71dca49 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 @@
}
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 @@
}
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 447be38..05d36f1 100644
--- a/rtc_base/synchronization/BUILD.gn
+++ b/rtc_base/synchronization/BUILD.gn
@@ -35,3 +35,27 @@
]
}
}
+
+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 0000000..5615915
--- /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 0000000..8146930
--- /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 0000000..d7e352b
--- /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 <thread> // 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<MockYieldHandler> 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<MockYieldHandler> 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<MockYieldHandler> main_handler;
+ EXPECT_CALL(main_handler, YieldExecution()).Times(1);
+ ScopedYieldPolicy policy(&main_handler);
+ events[2].Wait(Event::kForever);
+ other_thread.join();
+}
+} // namespace rtc