diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/api/task_queue | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/api/task_queue')
21 files changed, 2033 insertions, 0 deletions
diff --git a/third_party/libwebrtc/api/task_queue/BUILD.gn b/third_party/libwebrtc/api/task_queue/BUILD.gn new file mode 100644 index 0000000000..760ceaa3ef --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/BUILD.gn @@ -0,0 +1,171 @@ +# Copyright (c) 2018 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. + +import("../../webrtc.gni") + +rtc_library("task_queue") { + visibility = [ "*" ] + public = [ + "task_queue_base.h", + "task_queue_factory.h", + ] + sources = [ "task_queue_base.cc" ] + + deps = [ + "..:location", + "../../rtc_base:checks", + "../../rtc_base:macromagic", + "../../rtc_base/system:rtc_export", + "../units:time_delta", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/base:config", + "//third_party/abseil-cpp/absl/base:core_headers", + "//third_party/abseil-cpp/absl/functional:any_invocable", + "//third_party/abseil-cpp/absl/strings", + ] +} + +# Mozilla - we want to ensure that rtc_include_tests is set to false +# to guarantee that default_task_queue_factory is not used so we +# know that remaining win32k code in task_queue_win.cc is not built. +# See Bug 1797161 for more info. +assert(!rtc_include_tests, "Mozilla - verify rtc_include_tests is off") +if (rtc_include_tests) { +rtc_library("task_queue_test") { + visibility = [ "*" ] + testonly = true + sources = [ + "task_queue_test.cc", + "task_queue_test.h", + ] + + check_includes = false # no-presubmit-check TODO(bugs.webrtc.org/9419) + if (build_with_chromium) { + visibility = [] + visibility = webrtc_default_visibility + visibility += [ + # This is the only Chromium targets that can depend on this. The reason + # behind this is the fact that these are 'testonly' targets and as such + # it cannot be part of the WebRTC component. + "//components/webrtc:unit_tests", + "//third_party/blink/renderer/platform:blink_platform_unittests_sources", + ] + + # Don't depend on WebRTC code outside of webrtc_overrides:webrtc_component + # because this will break the WebRTC component build in Chromium. + deps = [ + "../../../webrtc_overrides:webrtc_component", + "../../test:test_support", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/cleanup", + "//third_party/abseil-cpp/absl/strings", + ] + } else { + deps = [ + ":default_task_queue_factory", + ":task_queue", + "../../api:field_trials_view", + "../../api:make_ref_counted", + "../../api/units:time_delta", + "../../rtc_base:refcount", + "../../rtc_base:rtc_event", + "../../rtc_base:timeutils", + "../../test:test_support", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/cleanup", + "//third_party/abseil-cpp/absl/strings", + ] + } +} +} + +rtc_library("default_task_queue_factory") { + visibility = [ "*" ] + if (!is_ios && !is_android) { + poisonous = [ "default_task_queue" ] + } + sources = [ "default_task_queue_factory.h" ] + deps = [ + ":task_queue", + "../../api:field_trials_view", + "../../rtc_base/memory:always_valid_pointer", + ] + + if (rtc_enable_libevent) { + if (is_android) { + sources += + [ "default_task_queue_factory_stdlib_or_libevent_experiment.cc" ] + deps += [ + "../../api/transport:field_trial_based_config", + "../../rtc_base:logging", + "../../rtc_base:rtc_task_queue_libevent", + "../../rtc_base:rtc_task_queue_stdlib", + ] + } else { + sources += [ "default_task_queue_factory_libevent.cc" ] + deps += [ "../../rtc_base:rtc_task_queue_libevent" ] + } + } else if (is_mac || is_ios) { + sources += [ "default_task_queue_factory_gcd.cc" ] + deps += [ "../../rtc_base:rtc_task_queue_gcd" ] + } else if (is_win && current_os != "winuwp") { + sources += [ "default_task_queue_factory_win.cc" ] + deps += [ "../../rtc_base:rtc_task_queue_win" ] + } else { + sources += [ "default_task_queue_factory_stdlib.cc" ] + deps += [ "../../rtc_base:rtc_task_queue_stdlib" ] + } +} + +rtc_library("pending_task_safety_flag") { + visibility = [ "*" ] + sources = [ + "pending_task_safety_flag.cc", + "pending_task_safety_flag.h", + ] + deps = [ + "../../api:refcountedbase", + "../../api:scoped_refptr", + "../../api:sequence_checker", + "../../rtc_base:checks", + "../../rtc_base/system:no_unique_address", + "../../rtc_base/system:rtc_export", + ] + absl_deps = [ + "//third_party/abseil-cpp/absl/base:nullability", + "//third_party/abseil-cpp/absl/functional:any_invocable", + ] +} + +if (rtc_include_tests) { + rtc_library("task_queue_default_factory_unittests") { + testonly = true + sources = [ "default_task_queue_factory_unittest.cc" ] + deps = [ + ":default_task_queue_factory", + ":task_queue_test", + "../../test:test_support", + ] + } + + rtc_library("pending_task_safety_flag_unittests") { + testonly = true + sources = [ "pending_task_safety_flag_unittest.cc" ] + deps = [ + ":pending_task_safety_flag", + "../../rtc_base:logging", + "../../rtc_base:rtc_event", + "../../rtc_base:rtc_task_queue", + "../../rtc_base:task_queue_for_test", + "../../test:test_support", + ] + } +} diff --git a/third_party/libwebrtc/api/task_queue/DEPS b/third_party/libwebrtc/api/task_queue/DEPS new file mode 100644 index 0000000000..1365edb504 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/DEPS @@ -0,0 +1,14 @@ +specific_include_rules = { + "task_queue_base\.h": [ + # Make TaskQueueBase RTC_LOCKABALE to allow annotate variables are only + # accessed on specific task queue. + "+rtc_base/thread_annotations.h", + ], + "task_queue_test\.h": [ + "+test/gtest.h", + ], + "pending_task_safety_flag.h": [ + "+rtc_base/checks.h", + "+rtc_base/system/no_unique_address.h", + ], +} diff --git a/third_party/libwebrtc/api/task_queue/default_task_queue_factory.h b/third_party/libwebrtc/api/task_queue/default_task_queue_factory.h new file mode 100644 index 0000000000..1d2dbd7ec2 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/default_task_queue_factory.h @@ -0,0 +1,25 @@ +/* + * 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 API_TASK_QUEUE_DEFAULT_TASK_QUEUE_FACTORY_H_ +#define API_TASK_QUEUE_DEFAULT_TASK_QUEUE_FACTORY_H_ + +#include <memory> + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" + +namespace webrtc { + +std::unique_ptr<TaskQueueFactory> CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials = nullptr); + +} // namespace webrtc + +#endif // API_TASK_QUEUE_DEFAULT_TASK_QUEUE_FACTORY_H_ diff --git a/third_party/libwebrtc/api/task_queue/default_task_queue_factory_gcd.cc b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_gcd.cc new file mode 100644 index 0000000000..391f09b393 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_gcd.cc @@ -0,0 +1,23 @@ +/* + * 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 <memory> + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" +#include "rtc_base/task_queue_gcd.h" + +namespace webrtc { + +std::unique_ptr<TaskQueueFactory> CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials) { + return CreateTaskQueueGcdFactory(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/api/task_queue/default_task_queue_factory_libevent.cc b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_libevent.cc new file mode 100644 index 0000000000..89079f51ca --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_libevent.cc @@ -0,0 +1,23 @@ +/* + * 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 <memory> + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" +#include "rtc_base/task_queue_libevent.h" + +namespace webrtc { + +std::unique_ptr<TaskQueueFactory> CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials) { + return CreateTaskQueueLibeventFactory(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/api/task_queue/default_task_queue_factory_stdlib.cc b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_stdlib.cc new file mode 100644 index 0000000000..10cda7c5ec --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_stdlib.cc @@ -0,0 +1,23 @@ +/* + * 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 <memory> + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" +#include "rtc_base/task_queue_stdlib.h" + +namespace webrtc { + +std::unique_ptr<TaskQueueFactory> CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials) { + return CreateTaskQueueStdlibFactory(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/api/task_queue/default_task_queue_factory_stdlib_or_libevent_experiment.cc b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_stdlib_or_libevent_experiment.cc new file mode 100644 index 0000000000..dc6e835907 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_stdlib_or_libevent_experiment.cc @@ -0,0 +1,38 @@ +/* + * Copyright 2022 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 <memory> + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" +#include "api/transport/field_trial_based_config.h" +#include "rtc_base/logging.h" +#include "rtc_base/memory/always_valid_pointer.h" +#include "rtc_base/task_queue_libevent.h" +#include "rtc_base/task_queue_stdlib.h" + +namespace webrtc { + +std::unique_ptr<TaskQueueFactory> CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials_view) { + AlwaysValidPointer<const FieldTrialsView, FieldTrialBasedConfig> field_trials( + field_trials_view); + if (field_trials->IsEnabled("WebRTC-TaskQueue-ReplaceLibeventWithStdlib")) { + RTC_LOG(LS_INFO) << "WebRTC-TaskQueue-ReplaceLibeventWithStdlib: " + << "using TaskQueueStdlibFactory."; + return CreateTaskQueueStdlibFactory(); + } + + RTC_LOG(LS_INFO) << "WebRTC-TaskQueue-ReplaceLibeventWithStdlib: " + << "using TaskQueueLibeventFactory."; + return CreateTaskQueueLibeventFactory(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/api/task_queue/default_task_queue_factory_unittest.cc b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_unittest.cc new file mode 100644 index 0000000000..92c17d8444 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_unittest.cc @@ -0,0 +1,24 @@ +/* + * 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 "api/task_queue/default_task_queue_factory.h" + +#include "api/task_queue/task_queue_test.h" +#include "test/gtest.h" + +namespace webrtc { +namespace { + +INSTANTIATE_TEST_SUITE_P(Default, + TaskQueueTest, + ::testing::Values(CreateDefaultTaskQueueFactory)); + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/api/task_queue/default_task_queue_factory_win.cc b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_win.cc new file mode 100644 index 0000000000..e3adc07327 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/default_task_queue_factory_win.cc @@ -0,0 +1,23 @@ +/* + * 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 <memory> + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" +#include "rtc_base/task_queue_win.h" + +namespace webrtc { + +std::unique_ptr<TaskQueueFactory> CreateDefaultTaskQueueFactory( + const FieldTrialsView* field_trials) { + return CreateTaskQueueWinFactory(); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/api/task_queue/pending_task_safety_flag.cc b/third_party/libwebrtc/api/task_queue/pending_task_safety_flag.cc new file mode 100644 index 0000000000..4d8fc2b9f4 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/pending_task_safety_flag.cc @@ -0,0 +1,68 @@ +/* + * 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 "api/task_queue/pending_task_safety_flag.h" + +namespace webrtc { + +// static +rtc::scoped_refptr<PendingTaskSafetyFlag> PendingTaskSafetyFlag::CreateInternal( + bool alive) { + // Explicit new, to access private constructor. + return rtc::scoped_refptr<PendingTaskSafetyFlag>( + new PendingTaskSafetyFlag(alive)); +} + +// static +rtc::scoped_refptr<PendingTaskSafetyFlag> PendingTaskSafetyFlag::Create() { + return CreateInternal(true); +} + +rtc::scoped_refptr<PendingTaskSafetyFlag> +PendingTaskSafetyFlag::CreateDetached() { + rtc::scoped_refptr<PendingTaskSafetyFlag> safety_flag = CreateInternal(true); + safety_flag->main_sequence_.Detach(); + return safety_flag; +} + +// Creates a flag, but with its SequenceChecker explicitly initialized for +// a given task queue and the `alive()` flag specified. +rtc::scoped_refptr<PendingTaskSafetyFlag> +PendingTaskSafetyFlag::CreateAttachedToTaskQueue( + bool alive, + absl::Nonnull<TaskQueueBase*> attached_queue) { + RTC_DCHECK(attached_queue) << "Null TaskQueue provided"; + return rtc::scoped_refptr<PendingTaskSafetyFlag>( + new PendingTaskSafetyFlag(alive, attached_queue)); +} + +rtc::scoped_refptr<PendingTaskSafetyFlag> +PendingTaskSafetyFlag::CreateDetachedInactive() { + rtc::scoped_refptr<PendingTaskSafetyFlag> safety_flag = CreateInternal(false); + safety_flag->main_sequence_.Detach(); + return safety_flag; +} + +void PendingTaskSafetyFlag::SetNotAlive() { + RTC_DCHECK_RUN_ON(&main_sequence_); + alive_ = false; +} + +void PendingTaskSafetyFlag::SetAlive() { + RTC_DCHECK_RUN_ON(&main_sequence_); + alive_ = true; +} + +bool PendingTaskSafetyFlag::alive() const { + RTC_DCHECK_RUN_ON(&main_sequence_); + return alive_; +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/api/task_queue/pending_task_safety_flag.h b/third_party/libwebrtc/api/task_queue/pending_task_safety_flag.h new file mode 100644 index 0000000000..12b1e00ee4 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/pending_task_safety_flag.h @@ -0,0 +1,179 @@ +/* + * 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 API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ +#define API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ + +#include <utility> + +#include "absl/base/nullability.h" +#include "absl/functional/any_invocable.h" +#include "api/ref_counted_base.h" +#include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "rtc_base/checks.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/system/rtc_export.h" + +namespace webrtc { + +// The PendingTaskSafetyFlag and the ScopedTaskSafety are designed to address +// the issue where you have a task to be executed later that has references, +// but cannot guarantee that the referenced object is alive when the task is +// executed. + +// This mechanism can be used with tasks that are created and destroyed +// on a single thread / task queue, and with tasks posted to the same +// thread/task queue, but tasks can be posted from any thread/TQ. + +// Typical usage: +// When posting a task, post a copy (capture by-value in a lambda) of the flag +// reference and before performing the work, check the `alive()` state. Abort if +// alive() returns `false`: +// +// class ExampleClass { +// .... +// rtc::scoped_refptr<PendingTaskSafetyFlag> flag = safety_flag_; +// my_task_queue_->PostTask( +// [flag = std::move(flag), this] { +// // Now running on the main thread. +// if (!flag->alive()) +// return; +// MyMethod(); +// }); +// .... +// ~ExampleClass() { +// safety_flag_->SetNotAlive(); +// } +// scoped_refptr<PendingTaskSafetyFlag> safety_flag_ +// = PendingTaskSafetyFlag::Create(); +// } +// +// SafeTask makes this check automatic: +// +// my_task_queue_->PostTask(SafeTask(safety_flag_, [this] { MyMethod(); })); +// +class RTC_EXPORT PendingTaskSafetyFlag final + : public rtc::RefCountedNonVirtual<PendingTaskSafetyFlag> { + public: + static rtc::scoped_refptr<PendingTaskSafetyFlag> Create(); + + // Creates a flag, but with its SequenceChecker initially detached. Hence, it + // may be created on a different thread than the flag will be used on. + static rtc::scoped_refptr<PendingTaskSafetyFlag> CreateDetached(); + + // Creates a flag, but with its SequenceChecker explicitly initialized for + // a given task queue and the `alive()` flag specified. + static rtc::scoped_refptr<PendingTaskSafetyFlag> CreateAttachedToTaskQueue( + bool alive, + absl::Nonnull<TaskQueueBase*> attached_queue); + + // Same as `CreateDetached()` except the initial state of the returned flag + // will be `!alive()`. + static rtc::scoped_refptr<PendingTaskSafetyFlag> CreateDetachedInactive(); + + ~PendingTaskSafetyFlag() = default; + + void SetNotAlive(); + // The SetAlive method is intended to support Start/Stop/Restart usecases. + // When a class has called SetNotAlive on a flag used for posted tasks, and + // decides it wants to post new tasks and have them run, there are two + // reasonable ways to do that: + // + // (i) Use the below SetAlive method. One subtlety is that any task posted + // prior to SetNotAlive, and still in the queue, is resurrected and will + // run. + // + // (ii) Create a fresh flag, and just drop the reference to the old one. This + // avoids the above problem, and ensures that tasks poster prior to + // SetNotAlive stay cancelled. Instead, there's a potential data race on + // the flag pointer itself. Some synchronization is required between the + // thread overwriting the flag pointer, and the threads that want to post + // tasks and therefore read that same pointer. + void SetAlive(); + bool alive() const; + + protected: + explicit PendingTaskSafetyFlag(bool alive) : alive_(alive) {} + PendingTaskSafetyFlag(bool alive, + absl::Nonnull<TaskQueueBase*> attached_queue) + : alive_(alive), main_sequence_(attached_queue) {} + + private: + static rtc::scoped_refptr<PendingTaskSafetyFlag> CreateInternal(bool alive); + + bool alive_ = true; + RTC_NO_UNIQUE_ADDRESS SequenceChecker main_sequence_; +}; + +// The ScopedTaskSafety makes using PendingTaskSafetyFlag very simple. +// It does automatic PTSF creation and signalling of destruction when the +// ScopedTaskSafety instance goes out of scope. +// +// Example usage: +// +// my_task_queue->PostTask(SafeTask(scoped_task_safety.flag(), +// [this] { +// // task goes here +// } +// +// This should be used by the class that wants tasks dropped after destruction. +// The requirement is that the instance has to be constructed and destructed on +// the same thread as the potentially dropped tasks would be running on. +class RTC_EXPORT ScopedTaskSafety final { + public: + ScopedTaskSafety() = default; + explicit ScopedTaskSafety(rtc::scoped_refptr<PendingTaskSafetyFlag> flag) + : flag_(std::move(flag)) {} + ~ScopedTaskSafety() { flag_->SetNotAlive(); } + + // Returns a new reference to the safety flag. + rtc::scoped_refptr<PendingTaskSafetyFlag> flag() const { return flag_; } + + // Marks the current flag as not-alive and attaches to a new one. + void reset(rtc::scoped_refptr<PendingTaskSafetyFlag> new_flag = + PendingTaskSafetyFlag::Create()) { + flag_->SetNotAlive(); + flag_ = std::move(new_flag); + } + + private: + rtc::scoped_refptr<PendingTaskSafetyFlag> flag_ = + PendingTaskSafetyFlag::Create(); +}; + +// Like ScopedTaskSafety, but allows construction on a different thread than +// where the flag will be used. +class RTC_EXPORT ScopedTaskSafetyDetached final { + public: + ScopedTaskSafetyDetached() = default; + ~ScopedTaskSafetyDetached() { flag_->SetNotAlive(); } + + // Returns a new reference to the safety flag. + rtc::scoped_refptr<PendingTaskSafetyFlag> flag() const { return flag_; } + + private: + rtc::scoped_refptr<PendingTaskSafetyFlag> flag_ = + PendingTaskSafetyFlag::CreateDetached(); +}; + +inline absl::AnyInvocable<void() &&> SafeTask( + rtc::scoped_refptr<PendingTaskSafetyFlag> flag, + absl::AnyInvocable<void() &&> task) { + return [flag = std::move(flag), task = std::move(task)]() mutable { + if (flag->alive()) { + std::move(task)(); + } + }; +} + +} // namespace webrtc + +#endif // API_TASK_QUEUE_PENDING_TASK_SAFETY_FLAG_H_ diff --git a/third_party/libwebrtc/api/task_queue/pending_task_safety_flag_gn/moz.build b/third_party/libwebrtc/api/task_queue/pending_task_safety_flag_gn/moz.build new file mode 100644 index 0000000000..52a2e5003b --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/pending_task_safety_flag_gn/moz.build @@ -0,0 +1,225 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1" +DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True +DEFINES["RTC_ENABLE_VP9"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_LIBRARY_IMPL"] = True +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0" + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "!/third_party/libwebrtc/gen", + "/ipc/chromium/src", + "/third_party/libwebrtc/", + "/third_party/libwebrtc/third_party/abseil-cpp/", + "/tools/profiler/public" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/api/task_queue/pending_task_safety_flag.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1" + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_GNU_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0" + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_AURA"] = "1" + DEFINES["USE_GLIB"] = "1" + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_OZONE"] = "1" + DEFINES["USE_UDEV"] = True + DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["_LARGEFILE64_SOURCE"] = True + DEFINES["_LARGEFILE_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_GLIB"] = "1" + DEFINES["USE_OZONE"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["_LARGEFILE64_SOURCE"] = True + DEFINES["_LARGEFILE_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "2" + DEFINES["RTC_ENABLE_WIN_WGC"] = True + DEFINES["UNICODE"] = True + DEFINES["USE_AURA"] = "1" + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP" + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_HAS_NODISCARD"] = True + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + +if CONFIG["TARGET_CPU"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["TARGET_CPU"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["TARGET_CPU"] == "mips32": + + DEFINES["MIPS32_LE"] = True + DEFINES["MIPS_FPU_LE"] = True + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["TARGET_CPU"] == "mips64": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["TARGET_CPU"] == "x86": + + DEFINES["WEBRTC_ENABLE_AVX2"] = True + +if CONFIG["TARGET_CPU"] == "x86_64": + + DEFINES["WEBRTC_ENABLE_AVX2"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0" + +if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_X11"] = "1" + +if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm": + + OS_LIBS += [ + "android_support", + "unwind" + ] + +if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86": + + CXXFLAGS += [ + "-msse2" + ] + + OS_LIBS += [ + "android_support" + ] + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "arm": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86": + + CXXFLAGS += [ + "-msse2" + ] + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86_64": + + DEFINES["_GNU_SOURCE"] = True + +Library("pending_task_safety_flag_gn") diff --git a/third_party/libwebrtc/api/task_queue/pending_task_safety_flag_unittest.cc b/third_party/libwebrtc/api/task_queue/pending_task_safety_flag_unittest.cc new file mode 100644 index 0000000000..3a1ed268fc --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/pending_task_safety_flag_unittest.cc @@ -0,0 +1,199 @@ +/* + * 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 "api/task_queue/pending_task_safety_flag.h" + +#include <memory> + +#include "rtc_base/event.h" +#include "rtc_base/logging.h" +#include "rtc_base/task_queue_for_test.h" +#include "test/gmock.h" +#include "test/gtest.h" + +namespace webrtc { + +TEST(PendingTaskSafetyFlagTest, Basic) { + rtc::scoped_refptr<PendingTaskSafetyFlag> safety_flag; + { + // Scope for the `owner` instance. + class Owner { + public: + Owner() = default; + ~Owner() { flag_->SetNotAlive(); } + + rtc::scoped_refptr<PendingTaskSafetyFlag> flag_ = + PendingTaskSafetyFlag::Create(); + } owner; + EXPECT_TRUE(owner.flag_->alive()); + safety_flag = owner.flag_; + EXPECT_TRUE(safety_flag->alive()); + } + // `owner` now out of scope. + EXPECT_FALSE(safety_flag->alive()); +} + +TEST(PendingTaskSafetyFlagTest, BasicScoped) { + rtc::scoped_refptr<PendingTaskSafetyFlag> safety_flag; + { + struct Owner { + ScopedTaskSafety safety; + } owner; + safety_flag = owner.safety.flag(); + EXPECT_TRUE(safety_flag->alive()); + } + // `owner` now out of scope. + EXPECT_FALSE(safety_flag->alive()); +} + +TEST(PendingTaskSafetyFlagTest, PendingTaskSuccess) { + TaskQueueForTest tq1("OwnerHere"); + TaskQueueForTest tq2("OwnerNotHere"); + + class Owner { + public: + Owner() : tq_main_(TaskQueueBase::Current()) { RTC_DCHECK(tq_main_); } + ~Owner() { + RTC_DCHECK(tq_main_->IsCurrent()); + flag_->SetNotAlive(); + } + + void DoStuff() { + RTC_DCHECK(!tq_main_->IsCurrent()); + rtc::scoped_refptr<PendingTaskSafetyFlag> safe = flag_; + tq_main_->PostTask([safe = std::move(safe), this]() { + if (!safe->alive()) + return; + stuff_done_ = true; + }); + } + + bool stuff_done() const { return stuff_done_; } + + private: + TaskQueueBase* const tq_main_; + bool stuff_done_ = false; + rtc::scoped_refptr<PendingTaskSafetyFlag> flag_ = + PendingTaskSafetyFlag::Create(); + }; + + std::unique_ptr<Owner> owner; + tq1.SendTask([&owner]() { + owner = std::make_unique<Owner>(); + EXPECT_FALSE(owner->stuff_done()); + }); + ASSERT_TRUE(owner); + tq2.SendTask([&owner]() { owner->DoStuff(); }); + tq1.SendTask([&owner]() { + EXPECT_TRUE(owner->stuff_done()); + owner.reset(); + }); + ASSERT_FALSE(owner); +} + +TEST(PendingTaskSafetyFlagTest, PendingTaskDropped) { + TaskQueueForTest tq1("OwnerHere"); + TaskQueueForTest tq2("OwnerNotHere"); + + class Owner { + public: + explicit Owner(bool* stuff_done) + : tq_main_(TaskQueueBase::Current()), stuff_done_(stuff_done) { + RTC_DCHECK(tq_main_); + *stuff_done_ = false; + } + ~Owner() { RTC_DCHECK(tq_main_->IsCurrent()); } + + void DoStuff() { + RTC_DCHECK(!tq_main_->IsCurrent()); + tq_main_->PostTask( + SafeTask(safety_.flag(), [this]() { *stuff_done_ = true; })); + } + + private: + TaskQueueBase* const tq_main_; + bool* const stuff_done_; + ScopedTaskSafety safety_; + }; + + std::unique_ptr<Owner> owner; + bool stuff_done = false; + tq1.SendTask([&owner, &stuff_done]() { + owner = std::make_unique<Owner>(&stuff_done); + }); + ASSERT_TRUE(owner); + // Queue up a task on tq1 that will execute before the 'DoStuff' task + // can, and delete the `owner` before the 'stuff' task can execute. + rtc::Event blocker; + tq1.PostTask([&blocker, &owner]() { + blocker.Wait(rtc::Event::kForever); + owner.reset(); + }); + + // Queue up a DoStuff... + tq2.SendTask([&owner]() { owner->DoStuff(); }); + + ASSERT_TRUE(owner); + blocker.Set(); + + // Run an empty task on tq1 to flush all the queued tasks. + tq1.WaitForPreviouslyPostedTasks(); + ASSERT_FALSE(owner); + EXPECT_FALSE(stuff_done); +} + +TEST(PendingTaskSafetyFlagTest, PendingTaskNotAliveInitialized) { + TaskQueueForTest tq("PendingTaskNotAliveInitialized"); + + // Create a new flag that initially not `alive`. + auto flag = PendingTaskSafetyFlag::CreateDetachedInactive(); + tq.SendTask([&flag]() { EXPECT_FALSE(flag->alive()); }); + + bool task_1_ran = false; + bool task_2_ran = false; + tq.PostTask(SafeTask(flag, [&task_1_ran]() { task_1_ran = true; })); + tq.PostTask([&flag]() { flag->SetAlive(); }); + tq.PostTask(SafeTask(flag, [&task_2_ran]() { task_2_ran = true; })); + + tq.WaitForPreviouslyPostedTasks(); + EXPECT_FALSE(task_1_ran); + EXPECT_TRUE(task_2_ran); +} + +TEST(PendingTaskSafetyFlagTest, PendingTaskInitializedForTaskQueue) { + TaskQueueForTest tq("PendingTaskAliveInitializedForTaskQueue"); + + // Create a new flag that initially `alive`, attached to a specific TQ. + auto flag = PendingTaskSafetyFlag::CreateAttachedToTaskQueue(true, tq.Get()); + tq.SendTask([&flag]() { EXPECT_TRUE(flag->alive()); }); + // Repeat the same steps but initialize as inactive. + flag = PendingTaskSafetyFlag::CreateAttachedToTaskQueue(false, tq.Get()); + tq.SendTask([&flag]() { EXPECT_FALSE(flag->alive()); }); +} + +TEST(PendingTaskSafetyFlagTest, SafeTask) { + rtc::scoped_refptr<PendingTaskSafetyFlag> flag = + PendingTaskSafetyFlag::Create(); + + int count = 0; + // Create two identical tasks that increment the `count`. + auto task1 = SafeTask(flag, [&count] { ++count; }); + auto task2 = SafeTask(flag, [&count] { ++count; }); + + EXPECT_EQ(count, 0); + std::move(task1)(); + EXPECT_EQ(count, 1); + flag->SetNotAlive(); + // Now task2 should actually not run. + std::move(task2)(); + EXPECT_EQ(count, 1); +} + +} // namespace webrtc diff --git a/third_party/libwebrtc/api/task_queue/task_queue_base.cc b/third_party/libwebrtc/api/task_queue/task_queue_base.cc new file mode 100644 index 0000000000..ecdc7f7691 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/task_queue_base.cc @@ -0,0 +1,81 @@ +/* + * 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 "api/task_queue/task_queue_base.h" + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/functional/any_invocable.h" +#include "api/units/time_delta.h" +#include "rtc_base/checks.h" + +#if defined(ABSL_HAVE_THREAD_LOCAL) + +namespace webrtc { +namespace { + +ABSL_CONST_INIT thread_local TaskQueueBase* current = nullptr; + +} // namespace + +TaskQueueBase* TaskQueueBase::Current() { + return current; +} + +TaskQueueBase::CurrentTaskQueueSetter::CurrentTaskQueueSetter( + TaskQueueBase* task_queue) + : previous_(current) { + current = task_queue; +} + +TaskQueueBase::CurrentTaskQueueSetter::~CurrentTaskQueueSetter() { + current = previous_; +} +} // namespace webrtc + +#elif defined(WEBRTC_POSIX) + +#include <pthread.h> + +namespace webrtc { +namespace { + +ABSL_CONST_INIT pthread_key_t g_queue_ptr_tls = 0; + +void InitializeTls() { + RTC_CHECK(pthread_key_create(&g_queue_ptr_tls, nullptr) == 0); +} + +pthread_key_t GetQueuePtrTls() { + static pthread_once_t init_once = PTHREAD_ONCE_INIT; + RTC_CHECK(pthread_once(&init_once, &InitializeTls) == 0); + return g_queue_ptr_tls; +} + +} // namespace + +TaskQueueBase* TaskQueueBase::Current() { + return static_cast<TaskQueueBase*>(pthread_getspecific(GetQueuePtrTls())); +} + +TaskQueueBase::CurrentTaskQueueSetter::CurrentTaskQueueSetter( + TaskQueueBase* task_queue) + : previous_(TaskQueueBase::Current()) { + pthread_setspecific(GetQueuePtrTls(), task_queue); +} + +TaskQueueBase::CurrentTaskQueueSetter::~CurrentTaskQueueSetter() { + pthread_setspecific(GetQueuePtrTls(), previous_); +} + +} // namespace webrtc + +#else +#error Unsupported platform +#endif diff --git a/third_party/libwebrtc/api/task_queue/task_queue_base.h b/third_party/libwebrtc/api/task_queue/task_queue_base.h new file mode 100644 index 0000000000..89e9e9e3b0 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/task_queue_base.h @@ -0,0 +1,198 @@ +/* + * 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 API_TASK_QUEUE_TASK_QUEUE_BASE_H_ +#define API_TASK_QUEUE_TASK_QUEUE_BASE_H_ + +#include <memory> +#include <utility> + +#include "absl/functional/any_invocable.h" +#include "api/location.h" +#include "api/units/time_delta.h" +#include "rtc_base/system/rtc_export.h" +#include "rtc_base/thread_annotations.h" + +namespace webrtc { + +// Asynchronously executes tasks in a way that guarantees that they're executed +// in FIFO order and that tasks never overlap. Tasks may always execute on the +// same worker thread and they may not. To DCHECK that tasks are executing on a +// known task queue, use IsCurrent(). +class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { + public: + enum class DelayPrecision { + // This may include up to a 17 ms leeway in addition to OS timer precision. + // See PostDelayedTask() for more information. + kLow, + // This does not have the additional delay that kLow has, but it is still + // limited by OS timer precision. See PostDelayedHighPrecisionTask() for + // more information. + kHigh, + }; + + // Starts destruction of the task queue. + // On return ensures no task are running and no new tasks are able to start + // on the task queue. + // Responsible for deallocation. Deallocation may happen synchronously during + // Delete or asynchronously after Delete returns. + // Code not running on the TaskQueue should not make any assumption when + // TaskQueue is deallocated and thus should not call any methods after Delete. + // Code running on the TaskQueue should not call Delete, but can assume + // TaskQueue still exists and may call other methods, e.g. PostTask. + // Should be called on the same task queue or thread that this task queue + // was created on. + virtual void Delete() = 0; + + // Schedules a `task` to execute. Tasks are executed in FIFO order. + // When a TaskQueue is deleted, pending tasks will not be executed but they + // will be deleted. + // + // As long as tasks are not posted from task destruction, posted tasks are + // guaranteed to be destroyed with Current() pointing to the task queue they + // were posted to, whether they're executed or not. That means SequenceChecker + // works during task destruction, a fact that can be used to guarantee + // thread-compatible object deletion happening on a particular task queue + // which can simplify class design. + // Note that this guarantee does not apply to delayed tasks. + // + // May be called on any thread or task queue, including this task queue. + void PostTask(absl::AnyInvocable<void() &&> task, + const Location& location = Location::Current()) { + PostTaskImpl(std::move(task), PostTaskTraits{}, location); + } + + // Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever + // possible. + // + // Schedules a `task` to execute a specified `delay` from when the call is + // made, using "low" precision. All scheduling is affected by OS-specific + // leeway and current workloads which means that in terms of precision there + // are no hard guarantees, but in addition to the OS induced leeway, "low" + // precision adds up to a 17 ms additional leeway. The purpose of this leeway + // is to achieve more efficient CPU scheduling and reduce Idle Wake Up + // frequency. + // + // The task may execute with [-1, 17 + OS induced leeway) ms additional delay. + // + // Avoid making assumptions about the precision of the OS scheduler. On macOS, + // the OS induced leeway may be 10% of sleep interval. On Windows, 1 ms + // precision timers may be used but there are cases, such as when running on + // battery, when the timer precision can be as poor as 15 ms. + // + // "Low" precision is not implemented everywhere yet. Where not yet + // implemented, PostDelayedTask() has "high" precision. See + // https://crbug.com/webrtc/13583 for more information. + // + // May be called on any thread or task queue, including this task queue. + void PostDelayedTask(absl::AnyInvocable<void() &&> task, + TimeDelta delay, + const Location& location = Location::Current()) { + PostDelayedTaskImpl(std::move(task), delay, PostDelayedTaskTraits{}, + location); + } + + // Prefer PostDelayedTask() over PostDelayedHighPrecisionTask() whenever + // possible. + // + // Schedules a `task` to execute a specified `delay` from when the call is + // made, using "high" precision. All scheduling is affected by OS-specific + // leeway and current workloads which means that in terms of precision there + // are no hard guarantees. + // + // The task may execute with [-1, OS induced leeway] ms additional delay. + // + // Avoid making assumptions about the precision of the OS scheduler. On macOS, + // the OS induced leeway may be 10% of sleep interval. On Windows, 1 ms + // precision timers may be used but there are cases, such as when running on + // battery, when the timer precision can be as poor as 15 ms. + // + // May be called on any thread or task queue, including this task queue. + void PostDelayedHighPrecisionTask( + absl::AnyInvocable<void() &&> task, + TimeDelta delay, + const Location& location = Location::Current()) { + PostDelayedTaskTraits traits; + traits.high_precision = true; + PostDelayedTaskImpl(std::move(task), delay, traits, location); + } + + // As specified by `precision`, calls either PostDelayedTask() or + // PostDelayedHighPrecisionTask(). + void PostDelayedTaskWithPrecision( + DelayPrecision precision, + absl::AnyInvocable<void() &&> task, + TimeDelta delay, + const Location& location = Location::Current()) { + switch (precision) { + case DelayPrecision::kLow: + PostDelayedTask(std::move(task), delay, location); + break; + case DelayPrecision::kHigh: + PostDelayedHighPrecisionTask(std::move(task), delay, location); + break; + } + } + + // Returns the task queue that is running the current thread. + // Returns nullptr if this thread is not associated with any task queue. + // May be called on any thread or task queue, including this task queue. + static TaskQueueBase* Current(); + bool IsCurrent() const { return Current() == this; } + + protected: + // This is currently only present here to simplify introduction of future + // planned task queue changes. + struct PostTaskTraits {}; + + struct PostDelayedTaskTraits { + // If `high_precision` is false, tasks may execute within up to a 17 ms + // leeway in addition to OS timer precision. Otherwise the task should be + // limited to OS timer precision. See PostDelayedTask() and + // PostDelayedHighPrecisionTask() for more information. + bool high_precision = false; + }; + + class RTC_EXPORT CurrentTaskQueueSetter { + public: + explicit CurrentTaskQueueSetter(TaskQueueBase* task_queue); + CurrentTaskQueueSetter(const CurrentTaskQueueSetter&) = delete; + CurrentTaskQueueSetter& operator=(const CurrentTaskQueueSetter&) = delete; + ~CurrentTaskQueueSetter(); + + private: + TaskQueueBase* const previous_; + }; + + // Subclasses should implement this method to support the behavior defined in + // the PostTask and PostTaskTraits docs above. + virtual void PostTaskImpl(absl::AnyInvocable<void() &&> task, + const PostTaskTraits& traits, + const Location& location) = 0; + + // Subclasses should implement this method to support the behavior defined in + // the PostDelayedTask/PostHighPrecisionDelayedTask and PostDelayedTaskTraits + // docs above. + virtual void PostDelayedTaskImpl(absl::AnyInvocable<void() &&> task, + TimeDelta delay, + const PostDelayedTaskTraits& traits, + const Location& location) = 0; + + // Users of the TaskQueue should call Delete instead of directly deleting + // this object. + virtual ~TaskQueueBase() = default; +}; + +struct TaskQueueDeleter { + void operator()(TaskQueueBase* task_queue) const { task_queue->Delete(); } +}; + +} // namespace webrtc + +#endif // API_TASK_QUEUE_TASK_QUEUE_BASE_H_ diff --git a/third_party/libwebrtc/api/task_queue/task_queue_factory.h b/third_party/libwebrtc/api/task_queue/task_queue_factory.h new file mode 100644 index 0000000000..b68ab33f69 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/task_queue_factory.h @@ -0,0 +1,35 @@ +/* + * 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 API_TASK_QUEUE_TASK_QUEUE_FACTORY_H_ +#define API_TASK_QUEUE_TASK_QUEUE_FACTORY_H_ + +#include <memory> + +#include "absl/strings/string_view.h" +#include "api/task_queue/task_queue_base.h" + +namespace webrtc { + +// The implementation of this interface must be thread-safe. +class TaskQueueFactory { + public: + // TaskQueue priority levels. On some platforms these will map to thread + // priorities, on others such as Mac and iOS, GCD queue priorities. + enum class Priority { NORMAL = 0, HIGH, LOW }; + + virtual ~TaskQueueFactory() = default; + virtual std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue( + absl::string_view name, + Priority priority) const = 0; +}; + +} // namespace webrtc + +#endif // API_TASK_QUEUE_TASK_QUEUE_FACTORY_H_ diff --git a/third_party/libwebrtc/api/task_queue/task_queue_gn/moz.build b/third_party/libwebrtc/api/task_queue/task_queue_gn/moz.build new file mode 100644 index 0000000000..73630a2ad9 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/task_queue_gn/moz.build @@ -0,0 +1,225 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + + ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ### + ### DO NOT edit it by hand. ### + +COMPILE_FLAGS["OS_INCLUDES"] = [] +AllowCompilerWarnings() + +DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1" +DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True +DEFINES["RTC_ENABLE_VP9"] = True +DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0" +DEFINES["WEBRTC_LIBRARY_IMPL"] = True +DEFINES["WEBRTC_MOZILLA_BUILD"] = True +DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0" +DEFINES["WEBRTC_STRICT_FIELD_TRIALS"] = "0" + +FINAL_LIBRARY = "webrtc" + + +LOCAL_INCLUDES += [ + "!/ipc/ipdl/_ipdlheaders", + "!/third_party/libwebrtc/gen", + "/ipc/chromium/src", + "/third_party/libwebrtc/", + "/third_party/libwebrtc/third_party/abseil-cpp/", + "/tools/profiler/public" +] + +UNIFIED_SOURCES += [ + "/third_party/libwebrtc/api/task_queue/task_queue_base.cc" +] + +if not CONFIG["MOZ_DEBUG"]: + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0" + DEFINES["NDEBUG"] = True + DEFINES["NVALGRIND"] = True + +if CONFIG["MOZ_DEBUG"] == "1": + + DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1" + +if CONFIG["OS_TARGET"] == "Android": + + DEFINES["ANDROID"] = True + DEFINES["ANDROID_NDK_VERSION_ROLL"] = "r22_1" + DEFINES["HAVE_SYS_UIO_H"] = True + DEFINES["WEBRTC_ANDROID"] = True + DEFINES["WEBRTC_ANDROID_OPENSLES"] = True + DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_GNU_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + + OS_LIBS += [ + "log" + ] + +if CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["WEBRTC_MAC"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True + DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0" + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_AURA"] = "1" + DEFINES["USE_GLIB"] = "1" + DEFINES["USE_NSS_CERTS"] = "1" + DEFINES["USE_OZONE"] = "1" + DEFINES["USE_UDEV"] = True + DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True + DEFINES["WEBRTC_LINUX"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["_LARGEFILE64_SOURCE"] = True + DEFINES["_LARGEFILE_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["USE_GLIB"] = "1" + DEFINES["USE_OZONE"] = "1" + DEFINES["USE_X11"] = "1" + DEFINES["WEBRTC_BSD"] = True + DEFINES["WEBRTC_ENABLE_LIBEVENT"] = True + DEFINES["WEBRTC_POSIX"] = True + DEFINES["_FILE_OFFSET_BITS"] = "64" + DEFINES["_LARGEFILE64_SOURCE"] = True + DEFINES["_LARGEFILE_SOURCE"] = True + DEFINES["__STDC_CONSTANT_MACROS"] = True + DEFINES["__STDC_FORMAT_MACROS"] = True + +if CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True + DEFINES["NOMINMAX"] = True + DEFINES["NTDDI_VERSION"] = "0x0A000000" + DEFINES["PSAPI_VERSION"] = "2" + DEFINES["RTC_ENABLE_WIN_WGC"] = True + DEFINES["UNICODE"] = True + DEFINES["USE_AURA"] = "1" + DEFINES["WEBRTC_WIN"] = True + DEFINES["WIN32"] = True + DEFINES["WIN32_LEAN_AND_MEAN"] = True + DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP" + DEFINES["WINVER"] = "0x0A00" + DEFINES["_ATL_NO_OPENGL"] = True + DEFINES["_CRT_RAND_S"] = True + DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True + DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True + DEFINES["_HAS_EXCEPTIONS"] = "0" + DEFINES["_HAS_NODISCARD"] = True + DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True + DEFINES["_SECURE_ATL"] = True + DEFINES["_UNICODE"] = True + DEFINES["_WIN32_WINNT"] = "0x0A00" + DEFINES["_WINDOWS"] = True + DEFINES["__STD_C"] = True + +if CONFIG["TARGET_CPU"] == "aarch64": + + DEFINES["WEBRTC_ARCH_ARM64"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["TARGET_CPU"] == "arm": + + CXXFLAGS += [ + "-mfpu=neon" + ] + + DEFINES["WEBRTC_ARCH_ARM"] = True + DEFINES["WEBRTC_ARCH_ARM_V7"] = True + DEFINES["WEBRTC_HAS_NEON"] = True + +if CONFIG["TARGET_CPU"] == "mips32": + + DEFINES["MIPS32_LE"] = True + DEFINES["MIPS_FPU_LE"] = True + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["TARGET_CPU"] == "mips64": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["TARGET_CPU"] == "x86": + + DEFINES["WEBRTC_ENABLE_AVX2"] = True + +if CONFIG["TARGET_CPU"] == "x86_64": + + DEFINES["WEBRTC_ENABLE_AVX2"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Android": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD": + + DEFINES["_DEBUG"] = True + +if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT": + + DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0" + +if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux": + + DEFINES["USE_X11"] = "1" + +if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "arm": + + OS_LIBS += [ + "android_support", + "unwind" + ] + +if CONFIG["OS_TARGET"] == "Android" and CONFIG["TARGET_CPU"] == "x86": + + CXXFLAGS += [ + "-msse2" + ] + + OS_LIBS += [ + "android_support" + ] + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "aarch64": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "arm": + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86": + + CXXFLAGS += [ + "-msse2" + ] + + DEFINES["_GNU_SOURCE"] = True + +if CONFIG["OS_TARGET"] == "Linux" and CONFIG["TARGET_CPU"] == "x86_64": + + DEFINES["_GNU_SOURCE"] = True + +Library("task_queue_gn") diff --git a/third_party/libwebrtc/api/task_queue/task_queue_test.cc b/third_party/libwebrtc/api/task_queue/task_queue_test.cc new file mode 100644 index 0000000000..b02333ec58 --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/task_queue_test.cc @@ -0,0 +1,354 @@ +/* + * 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 "api/task_queue/task_queue_test.h" + +#include <memory> + +#include "absl/cleanup/cleanup.h" +#include "absl/strings/string_view.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" +#include "rtc_base/event.h" +#include "rtc_base/ref_counter.h" +#include "rtc_base/time_utils.h" + +namespace webrtc { +namespace { + +// Avoids a dependency to system_wrappers. +void SleepFor(TimeDelta duration) { + rtc::ScopedAllowBaseSyncPrimitivesForTesting allow; + rtc::Event event; + event.Wait(duration); +} + +std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue( + const std::unique_ptr<webrtc::TaskQueueFactory>& factory, + absl::string_view task_queue_name, + TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::NORMAL) { + return factory->CreateTaskQueue(task_queue_name, priority); +} + +TEST_P(TaskQueueTest, Construct) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + auto queue = CreateTaskQueue(factory, "Construct"); + EXPECT_FALSE(queue->IsCurrent()); +} + +TEST_P(TaskQueueTest, PostAndCheckCurrent) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + rtc::Event event; + auto queue = CreateTaskQueue(factory, "PostAndCheckCurrent"); + + // We're not running a task, so `queue` shouldn't be current. + // Note that because rtc::Thread also supports the TQ interface and + // TestMainImpl::Init wraps the main test thread (bugs.webrtc.org/9714), that + // means that TaskQueueBase::Current() will still return a valid value. + EXPECT_FALSE(queue->IsCurrent()); + + queue->PostTask([&event, &queue] { + EXPECT_TRUE(queue->IsCurrent()); + event.Set(); + }); + EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); +} + +TEST_P(TaskQueueTest, PostCustomTask) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + rtc::Event ran; + auto queue = CreateTaskQueue(factory, "PostCustomImplementation"); + + class CustomTask { + public: + explicit CustomTask(rtc::Event* ran) : ran_(ran) {} + + void operator()() { ran_->Set(); } + + private: + rtc::Event* const ran_; + } my_task(&ran); + + queue->PostTask(my_task); + EXPECT_TRUE(ran.Wait(TimeDelta::Seconds(1))); +} + +TEST_P(TaskQueueTest, PostDelayedZero) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + rtc::Event event; + auto queue = CreateTaskQueue(factory, "PostDelayedZero"); + + queue->PostDelayedTask([&event] { event.Set(); }, TimeDelta::Zero()); + EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); +} + +TEST_P(TaskQueueTest, PostFromQueue) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + rtc::Event event; + auto queue = CreateTaskQueue(factory, "PostFromQueue"); + + queue->PostTask( + [&event, &queue] { queue->PostTask([&event] { event.Set(); }); }); + EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); +} + +TEST_P(TaskQueueTest, PostDelayed) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + rtc::Event event; + auto queue = + CreateTaskQueue(factory, "PostDelayed", TaskQueueFactory::Priority::HIGH); + + int64_t start = rtc::TimeMillis(); + queue->PostDelayedTask( + [&event, &queue] { + EXPECT_TRUE(queue->IsCurrent()); + event.Set(); + }, + TimeDelta::Millis(100)); + EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); + int64_t end = rtc::TimeMillis(); + // These tests are a little relaxed due to how "powerful" our test bots can + // be. Most recently we've seen windows bots fire the callback after 94-99ms, + // which is why we have a little bit of leeway backwards as well. + EXPECT_GE(end - start, 90u); + EXPECT_NEAR(end - start, 190u, 100u); // Accept 90-290. +} + +TEST_P(TaskQueueTest, PostMultipleDelayed) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + auto queue = CreateTaskQueue(factory, "PostMultipleDelayed"); + + std::vector<rtc::Event> events(100); + for (int i = 0; i < 100; ++i) { + rtc::Event* event = &events[i]; + queue->PostDelayedTask( + [event, &queue] { + EXPECT_TRUE(queue->IsCurrent()); + event->Set(); + }, + TimeDelta::Millis(i)); + } + + for (rtc::Event& e : events) + EXPECT_TRUE(e.Wait(TimeDelta::Seconds(1))); +} + +TEST_P(TaskQueueTest, PostDelayedAfterDestruct) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + rtc::Event run; + rtc::Event deleted; + auto queue = CreateTaskQueue(factory, "PostDelayedAfterDestruct"); + absl::Cleanup cleanup = [&deleted] { deleted.Set(); }; + queue->PostDelayedTask([&run, cleanup = std::move(cleanup)] { run.Set(); }, + TimeDelta::Millis(100)); + // Destroy the queue. + queue = nullptr; + // Task might outlive the TaskQueue, but still should be deleted. + EXPECT_TRUE(deleted.Wait(TimeDelta::Seconds(1))); + EXPECT_FALSE(run.Wait(TimeDelta::Zero())); // and should not run. +} + +TEST_P(TaskQueueTest, PostDelayedHighPrecisionAfterDestruct) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + rtc::Event run; + rtc::Event deleted; + auto queue = + CreateTaskQueue(factory, "PostDelayedHighPrecisionAfterDestruct"); + absl::Cleanup cleanup = [&deleted] { deleted.Set(); }; + queue->PostDelayedHighPrecisionTask( + [&run, cleanup = std::move(cleanup)] { run.Set(); }, + TimeDelta::Millis(100)); + // Destroy the queue. + queue = nullptr; + // Task might outlive the TaskQueue, but still should be deleted. + EXPECT_TRUE(deleted.Wait(TimeDelta::Seconds(1))); + EXPECT_FALSE(run.Wait(TimeDelta::Zero())); // and should not run. +} + +TEST_P(TaskQueueTest, PostedUnexecutedClosureDestroyedOnTaskQueue) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + auto queue = + CreateTaskQueue(factory, "PostedUnexecutedClosureDestroyedOnTaskQueue"); + TaskQueueBase* queue_ptr = queue.get(); + queue->PostTask([] { SleepFor(TimeDelta::Millis(100)); }); + // Give the task queue a chance to start executing the first lambda. + SleepFor(TimeDelta::Millis(10)); + rtc::Event finished; + // Then ensure the next lambda (which is likely not executing yet) is + // destroyed in the task queue context when the queue is deleted. + auto cleanup = absl::Cleanup([queue_ptr, &finished] { + EXPECT_EQ(queue_ptr, TaskQueueBase::Current()); + finished.Set(); + }); + queue->PostTask([cleanup = std::move(cleanup)] {}); + queue = nullptr; + finished.Wait(TimeDelta::Seconds(1)); +} + +TEST_P(TaskQueueTest, PostedClosureDestroyedOnTaskQueue) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + auto queue = CreateTaskQueue(factory, "PostedClosureDestroyedOnTaskQueue"); + TaskQueueBase* queue_ptr = queue.get(); + rtc::Event finished; + auto cleanup = absl::Cleanup([queue_ptr, &finished] { + EXPECT_EQ(queue_ptr, TaskQueueBase::Current()); + finished.Set(); + }); + // The cleanup task may or may not have had time to execute when the task + // queue is destroyed. Regardless, the task should be destroyed on the + // queue. + queue->PostTask([cleanup = std::move(cleanup)] {}); + queue = nullptr; + finished.Wait(TimeDelta::Seconds(1)); +} + +TEST_P(TaskQueueTest, PostedExecutedClosureDestroyedOnTaskQueue) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + auto queue = + CreateTaskQueue(factory, "PostedExecutedClosureDestroyedOnTaskQueue"); + TaskQueueBase* queue_ptr = queue.get(); + // Ensure an executed lambda is destroyed on the task queue. + rtc::Event finished; + queue->PostTask([cleanup = absl::Cleanup([queue_ptr, &finished] { + EXPECT_EQ(queue_ptr, TaskQueueBase::Current()); + finished.Set(); + })] {}); + finished.Wait(TimeDelta::Seconds(1)); +} + +TEST_P(TaskQueueTest, PostAndReuse) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + rtc::Event event; + auto post_queue = CreateTaskQueue(factory, "PostQueue"); + auto reply_queue = CreateTaskQueue(factory, "ReplyQueue"); + + int call_count = 0; + + class ReusedTask { + public: + ReusedTask(int* counter, TaskQueueBase* reply_queue, rtc::Event* event) + : counter_(*counter), reply_queue_(reply_queue), event_(*event) { + EXPECT_EQ(counter_, 0); + } + ReusedTask(ReusedTask&&) = default; + ReusedTask& operator=(ReusedTask&&) = delete; + + void operator()() && { + if (++counter_ == 1) { + reply_queue_->PostTask(std::move(*this)); + // At this point, the object is in the moved-from state. + } else { + EXPECT_EQ(counter_, 2); + EXPECT_TRUE(reply_queue_->IsCurrent()); + event_.Set(); + } + } + + private: + int& counter_; + TaskQueueBase* const reply_queue_; + rtc::Event& event_; + }; + + ReusedTask task(&call_count, reply_queue.get(), &event); + post_queue->PostTask(std::move(task)); + EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1))); +} + +TEST_P(TaskQueueTest, PostALot) { + // Waits until DecrementCount called `count` times. Thread safe. + class BlockingCounter { + public: + explicit BlockingCounter(int initial_count) : count_(initial_count) {} + + void DecrementCount() { + if (count_.DecRef() == rtc::RefCountReleaseStatus::kDroppedLastRef) { + event_.Set(); + } + } + bool Wait(TimeDelta give_up_after) { return event_.Wait(give_up_after); } + + private: + webrtc_impl::RefCounter count_; + rtc::Event event_; + }; + + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + static constexpr int kTaskCount = 0xffff; + rtc::Event posting_done; + BlockingCounter all_destroyed(kTaskCount); + + int tasks_executed = 0; + auto task_queue = CreateTaskQueue(factory, "PostALot"); + + task_queue->PostTask([&] { + // Post tasks from the queue to guarantee that the 1st task won't be + // executed before the last one is posted. + for (int i = 0; i < kTaskCount; ++i) { + absl::Cleanup cleanup = [&] { all_destroyed.DecrementCount(); }; + task_queue->PostTask([&tasks_executed, cleanup = std::move(cleanup)] { + ++tasks_executed; + }); + } + + posting_done.Set(); + }); + + // Before destroying the task queue wait until all child tasks are posted. + posting_done.Wait(rtc::Event::kForever); + // Destroy the task queue. + task_queue = nullptr; + + // Expect all tasks are destroyed eventually. In some task queue + // implementations that might happen on a different thread after task queue is + // destroyed. + EXPECT_TRUE(all_destroyed.Wait(TimeDelta::Minutes(1))); + EXPECT_LE(tasks_executed, kTaskCount); +} + +// Test posting two tasks that have shared state not protected by a +// lock. The TaskQueue should guarantee memory read-write order and +// FIFO task execution order, so the second task should always see the +// changes that were made by the first task. +// +// If the TaskQueue doesn't properly synchronize the execution of +// tasks, there will be a data race, which is undefined behavior. The +// EXPECT calls may randomly catch this, but to make the most of this +// unit test, run it under TSan or some other tool that is able to +// directly detect data races. +TEST_P(TaskQueueTest, PostTwoWithSharedUnprotectedState) { + std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr); + struct SharedState { + // First task will set this value to 1 and second will assert it. + int state = 0; + } state; + + auto queue = CreateTaskQueue(factory, "PostTwoWithSharedUnprotectedState"); + rtc::Event done; + queue->PostTask([&state, &queue, &done] { + // Post tasks from queue to guarantee, that 1st task won't be + // executed before the second one will be posted. + queue->PostTask([&state] { state.state = 1; }); + queue->PostTask([&state, &done] { + EXPECT_EQ(state.state, 1); + done.Set(); + }); + // Check, that state changing tasks didn't start yet. + EXPECT_EQ(state.state, 0); + }); + EXPECT_TRUE(done.Wait(TimeDelta::Seconds(1))); +} + +// TaskQueueTest is a set of tests for any implementation of the TaskQueueBase. +// Tests are instantiated next to the concrete implementation(s). +// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#creating-value-parameterized-abstract-tests +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TaskQueueTest); + +} // namespace +} // namespace webrtc diff --git a/third_party/libwebrtc/api/task_queue/task_queue_test.h b/third_party/libwebrtc/api/task_queue/task_queue_test.h new file mode 100644 index 0000000000..214f95008f --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/task_queue_test.h @@ -0,0 +1,41 @@ +/* + * 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 API_TASK_QUEUE_TASK_QUEUE_TEST_H_ +#define API_TASK_QUEUE_TASK_QUEUE_TEST_H_ + +#include <functional> +#include <memory> + +#include "api/field_trials_view.h" +#include "api/task_queue/task_queue_factory.h" +#include "test/gtest.h" + +namespace webrtc { + +// Suite of tests to verify TaskQueue implementation with. +// Example usage: +// +// namespace { +// +// using ::testing::Values; +// using ::webrtc::TaskQueueTest; +// +// std::unique_ptr<webrtc::TaskQueueFactory> CreateMyFactory(); +// +// INSTANTIATE_TEST_SUITE_P(My, TaskQueueTest, Values(CreateMyFactory)); +// +// } // namespace +class TaskQueueTest + : public ::testing::TestWithParam<std::function< + std::unique_ptr<TaskQueueFactory>(const FieldTrialsView*)>> {}; + +} // namespace webrtc + +#endif // API_TASK_QUEUE_TASK_QUEUE_TEST_H_ diff --git a/third_party/libwebrtc/api/task_queue/test/BUILD.gn b/third_party/libwebrtc/api/task_queue/test/BUILD.gn new file mode 100644 index 0000000000..25f7ed0c7f --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/test/BUILD.gn @@ -0,0 +1,20 @@ +# Copyright (c) 2018 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. + +import("../../../webrtc.gni") + +rtc_library("mock_task_queue_base") { + testonly = true + sources = [ "mock_task_queue_base.h" ] + deps = [ + "../../../api/task_queue:task_queue", + "../../../api/units:time_delta", + "../../../test:test_support", + ] + absl_deps = [ "//third_party/abseil-cpp/absl/functional:any_invocable" ] +} diff --git a/third_party/libwebrtc/api/task_queue/test/mock_task_queue_base.h b/third_party/libwebrtc/api/task_queue/test/mock_task_queue_base.h new file mode 100644 index 0000000000..0540afe16b --- /dev/null +++ b/third_party/libwebrtc/api/task_queue/test/mock_task_queue_base.h @@ -0,0 +1,44 @@ +/* + * Copyright 2022 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 API_TASK_QUEUE_TEST_MOCK_TASK_QUEUE_BASE_H_ +#define API_TASK_QUEUE_TEST_MOCK_TASK_QUEUE_BASE_H_ + +#include "absl/functional/any_invocable.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" +#include "test/gmock.h" + +namespace webrtc { + +class MockTaskQueueBase : public TaskQueueBase { + public: + using TaskQueueBase::PostDelayedTaskTraits; + using TaskQueueBase::PostTaskTraits; + + MOCK_METHOD(void, Delete, (), (override)); + MOCK_METHOD(void, + PostTaskImpl, + (absl::AnyInvocable<void() &&>, + const PostTaskTraits&, + const Location&), + (override)); + MOCK_METHOD(void, + PostDelayedTaskImpl, + (absl::AnyInvocable<void() &&>, + TimeDelta, + const PostDelayedTaskTraits&, + const Location&), + (override)); +}; + +} // namespace webrtc + +#endif // API_TASK_QUEUE_TEST_MOCK_TASK_QUEUE_BASE_H_ |