diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2023-02-13 16:03:23 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2023-05-26 11:26:35 +0000 |
commit | 813d9ae984a99e739b99cf694a9d5b24d0a6b7a7 (patch) | |
tree | 60c14d40d77a3c702c8a72887662d97c0b8f3e99 /chromium/base/task | |
parent | eb596ba9fe579987eb93f6b4021ca156885b48c2 (diff) |
BASELINE: Update Chromium to 110.0.5481.111
Change-Id: I2b5f5ed66fee2a6f8da61c9b17fd1b25bb5b3a4e
Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/464348
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/base/task')
96 files changed, 2103 insertions, 2790 deletions
diff --git a/chromium/base/task/bind_post_task_unittest.cc b/chromium/base/task/bind_post_task_unittest.cc index 33434ebb54a..f09072efaa2 100644 --- a/chromium/base/task/bind_post_task_unittest.cc +++ b/chromium/base/task/bind_post_task_unittest.cc @@ -6,10 +6,10 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/memory/raw_ref.h" #include "base/sequence_checker_impl.h" #include "base/task/sequenced_task_runner.h" #include "base/test/task_environment.h" -#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -41,14 +41,14 @@ class SequenceRestrictionChecker { ~SequenceRestrictionChecker() { EXPECT_TRUE(checker_.CalledOnValidSequence()); - set_on_destroy_ = true; + *set_on_destroy_ = true; } void Run() { EXPECT_TRUE(checker_.CalledOnValidSequence()); } private: SequenceCheckerImpl checker_; - bool& set_on_destroy_; + const raw_ref<bool> set_on_destroy_; }; } // namespace @@ -57,7 +57,7 @@ class BindPostTaskTest : public testing::Test { protected: test::SingleThreadTaskEnvironment task_environment_; scoped_refptr<SequencedTaskRunner> task_runner_ = - SequencedTaskRunnerHandle::Get(); + SequencedTaskRunner::GetCurrentDefault(); }; TEST_F(BindPostTaskTest, OnceClosure) { diff --git a/chromium/base/task/bind_post_task_unittest.nc b/chromium/base/task/bind_post_task_unittest.nc index 7d8a91a415e..5aa10960cbd 100644 --- a/chromium/base/task/bind_post_task_unittest.nc +++ b/chromium/base/task/bind_post_task_unittest.nc @@ -7,9 +7,9 @@ #include "base/task/bind_post_task.h" +#include "base/task/sequenced_task_runner.h" #include "base/bind.h" #include "base/callback.h" -#include "base/threading/sequenced_task_runner_handle.h" namespace base { @@ -21,7 +21,7 @@ int ReturnInt() { // OnceCallback with non-void return type. void WontCompile() { OnceCallback<int()> cb = BindOnce(&ReturnInt); - auto post_cb = BindPostTask(SequencedTaskRunnerHandle::Get(), std::move(cb)); + auto post_cb = BindPostTask(SequencedTaskRunner::GetCurrentDefault(), std::move(cb)); std::move(post_cb).Run(); } @@ -29,7 +29,7 @@ void WontCompile() { // RepeatingCallback with non-void return type. void WontCompile() { RepeatingCallback<int()> cb = BindRepeating(&ReturnInt); - auto post_cb = BindPostTask(SequencedTaskRunnerHandle::Get(), std::move(cb)); + auto post_cb = BindPostTask(SequencedTaskRunner::GetCurrentDefault(), std::move(cb)); std::move(post_cb).Run(); } diff --git a/chromium/base/task/cancelable_task_tracker.cc b/chromium/base/task/cancelable_task_tracker.cc index a04011aec5c..d28a8f5b6af 100644 --- a/chromium/base/task/cancelable_task_tracker.cc +++ b/chromium/base/task/cancelable_task_tracker.cc @@ -17,7 +17,6 @@ #include "base/task/scoped_set_task_priority_for_current_thread.h" #include "base/task/sequenced_task_runner.h" #include "base/task/task_runner.h" -#include "base/threading/sequenced_task_runner_handle.h" namespace base { @@ -111,7 +110,7 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply( CHECK(weak_this_); // We need a SequencedTaskRunnerHandle to run |reply|. - DCHECK(SequencedTaskRunnerHandle::IsSet()); + DCHECK(SequencedTaskRunner::HasCurrentDefault()); auto flag = MakeRefCounted<TaskCancellationFlag>(); @@ -124,10 +123,11 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply( BindOnce(&CancelableTaskTracker::Untrack, Unretained(this), id); bool success = task_runner->PostTaskAndReply( from_here, - BindOnce(&RunIfNotCanceled, SequencedTaskRunnerHandle::Get(), flag, - std::move(task)), - BindOnce(&RunThenUntrackIfNotCanceled, SequencedTaskRunnerHandle::Get(), - flag, std::move(reply), std::move(untrack_closure))); + BindOnce(&RunIfNotCanceled, SequencedTaskRunner::GetCurrentDefault(), + flag, std::move(task)), + BindOnce(&RunThenUntrackIfNotCanceled, + SequencedTaskRunner::GetCurrentDefault(), flag, std::move(reply), + std::move(untrack_closure))); if (!success) return kBadTaskId; @@ -139,7 +139,7 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply( CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( IsCanceledCallback* is_canceled_cb) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(SequencedTaskRunnerHandle::IsSet()); + DCHECK(SequencedTaskRunner::HasCurrentDefault()); TaskId id = next_id_; next_id_++; // int64_t is big enough that we ignore the potential overflow. @@ -152,13 +152,14 @@ CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId( BindOnce(&CancelableTaskTracker::Untrack, Unretained(this), id); // Will always run |untrack_closure| on current sequence. - ScopedClosureRunner untrack_runner( - BindOnce(&RunOrPostToTaskRunner, SequencedTaskRunnerHandle::Get(), - BindOnce(&RunIfNotCanceled, SequencedTaskRunnerHandle::Get(), - flag, std::move(untrack_closure)))); - - *is_canceled_cb = BindRepeating(&IsCanceled, SequencedTaskRunnerHandle::Get(), - flag, std::move(untrack_runner)); + ScopedClosureRunner untrack_runner(BindOnce( + &RunOrPostToTaskRunner, SequencedTaskRunner::GetCurrentDefault(), + BindOnce(&RunIfNotCanceled, SequencedTaskRunner::GetCurrentDefault(), + flag, std::move(untrack_closure)))); + + *is_canceled_cb = + BindRepeating(&IsCanceled, SequencedTaskRunner::GetCurrentDefault(), flag, + std::move(untrack_runner)); Track(id, std::move(flag)); return id; diff --git a/chromium/base/task/common/checked_lock.h b/chromium/base/task/common/checked_lock.h index 4f9bcd72876..7d6f75a5ca7 100644 --- a/chromium/base/task/common/checked_lock.h +++ b/chromium/base/task/common/checked_lock.h @@ -9,6 +9,7 @@ #include "base/check_op.h" #include "base/dcheck_is_on.h" +#include "base/memory/raw_ref.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "base/task/common/checked_lock_impl.h" @@ -125,7 +126,7 @@ class SCOPED_LOCKABLE AnnotateAcquiredLockAlias { EXCLUSIVE_LOCK_FUNCTION(lock_alias) : acquired_lock_(acquired_lock) { DCHECK_EQ(&acquired_lock, &lock_alias); - acquired_lock_.AssertAcquired(); + acquired_lock_->AssertAcquired(); } AnnotateAcquiredLockAlias(const AnnotateAcquiredLockAlias&) = delete; @@ -133,11 +134,11 @@ class SCOPED_LOCKABLE AnnotateAcquiredLockAlias { delete; ~AnnotateAcquiredLockAlias() UNLOCK_FUNCTION() { - acquired_lock_.AssertAcquired(); + acquired_lock_->AssertAcquired(); } private: - const CheckedLock& acquired_lock_; + const raw_ref<const CheckedLock> acquired_lock_; }; } // namespace internal diff --git a/chromium/base/task/common/checked_lock_impl.cc b/chromium/base/task/common/checked_lock_impl.cc index 71a5ee091f3..eae408a13f7 100644 --- a/chromium/base/task/common/checked_lock_impl.cc +++ b/chromium/base/task/common/checked_lock_impl.cc @@ -4,6 +4,7 @@ #include "base/task/common/checked_lock_impl.h" +#include <ostream> #include <unordered_map> #include <vector> diff --git a/chromium/base/task/common/operations_controller_unittest.cc b/chromium/base/task/common/operations_controller_unittest.cc index 1766dfcbf27..ce729a4954a 100644 --- a/chromium/base/task/common/operations_controller_unittest.cc +++ b/chromium/base/task/common/operations_controller_unittest.cc @@ -8,6 +8,7 @@ #include <cstdint> #include <utility> +#include "base/memory/raw_ref.h" #include "base/ranges/algorithm.h" #include "base/threading/platform_thread.h" #include "base/threading/simple_thread.h" @@ -20,10 +21,10 @@ namespace { class ScopedShutdown { public: ScopedShutdown(OperationsController* controller) : controller_(*controller) {} - ~ScopedShutdown() { controller_.ShutdownAndWaitForZeroOperations(); } + ~ScopedShutdown() { controller_->ShutdownAndWaitForZeroOperations(); } private: - OperationsController& controller_; + const raw_ref<OperationsController> controller_; }; TEST(OperationsControllerTest, CanBeDestroyedWithoutWaiting) { @@ -115,13 +116,13 @@ class TestThread : public SimpleThread { started_(*started), thread_counter_(*thread_counter) {} void Run() override { - thread_counter_.fetch_add(1, std::memory_order_relaxed); + thread_counter_->fetch_add(1, std::memory_order_relaxed); while (true) { PlatformThread::YieldCurrentThread(); - bool was_started = started_.load(std::memory_order_relaxed); + bool was_started = started_->load(std::memory_order_relaxed); std::vector<OperationsController::OperationToken> tokens; for (int i = 0; i < 100; ++i) { - tokens.push_back(controller_.TryBeginOperation()); + tokens.push_back(controller_->TryBeginOperation()); } if (!was_started) continue; @@ -132,9 +133,9 @@ class TestThread : public SimpleThread { } private: - OperationsController& controller_; - std::atomic<bool>& started_; - std::atomic<int32_t>& thread_counter_; + const raw_ref<OperationsController> controller_; + const raw_ref<std::atomic<bool>> started_; + const raw_ref<std::atomic<int32_t>> thread_counter_; }; TEST(OperationsControllerTest, BeginsFromMultipleThreads) { diff --git a/chromium/base/task/common/task_annotator.cc b/chromium/base/task/common/task_annotator.cc index 4c6e9117e28..9821fd29fbd 100644 --- a/chromium/base/task/common/task_annotator.cc +++ b/chromium/base/task/common/task_annotator.cc @@ -4,12 +4,15 @@ #include "base/task/common/task_annotator.h" +#include <stdint.h> +#include <algorithm> #include <array> #include "base/check_op.h" #include "base/debug/activity_tracker.h" #include "base/debug/alias.h" #include "base/hash/md5.h" +#include "base/logging.h" #include "base/no_destructor.h" #include "base/ranges/algorithm.h" #include "base/sys_byteorder.h" @@ -47,12 +50,32 @@ GetTLSForCurrentScopedIpcHash() { return instance.get(); } +ThreadLocalPointer<TaskAnnotator::LongTaskTracker>* +GetTLSForCurrentLongTaskTracker() { + static NoDestructor<ThreadLocalPointer<TaskAnnotator::LongTaskTracker>> + instance; + return instance.get(); +} + } // namespace const PendingTask* TaskAnnotator::CurrentTaskForThread() { return GetTLSForCurrentPendingTask()->Get(); } +void TaskAnnotator::OnIPCReceived(const char* interface_name, + uint32_t (*method_info)(), + bool is_response) { + base::TaskAnnotator::LongTaskTracker* current_long_task_tracker = + GetTLSForCurrentLongTaskTracker()->Get(); + + if (!current_long_task_tracker) + return; + + current_long_task_tracker->SetIpcDetails(interface_name, method_info, + is_response); +} + TaskAnnotator::TaskAnnotator() = default; TaskAnnotator::~TaskAnnotator() = default; @@ -186,7 +209,7 @@ void TaskAnnotator::ClearObserverForTesting() { // TRACE_EVENT argument helper, writing the task location data into // EventContext. void TaskAnnotator::EmitTaskLocation(perfetto::EventContext& ctx, - const PendingTask& task) const { + const PendingTask& task) { ctx.event()->set_task_execution()->set_posted_from_iid( base::trace_event::InternedSourceLocation::Get(&ctx, task.posted_from)); } @@ -256,4 +279,87 @@ TaskAnnotator::ScopedSetIpcHash::~ScopedSetIpcHash() { tls_ipc_hash->Set(old_scoped_ipc_hash_.get()); } +TaskAnnotator::LongTaskTracker::LongTaskTracker(const TickClock* tick_clock, + PendingTask& pending_task, + TaskAnnotator* task_annotator) + : tick_clock_(tick_clock), + pending_task_(pending_task), + task_annotator_(task_annotator) { + auto* tls_long_task_tracker = GetTLSForCurrentLongTaskTracker(); + old_long_task_tracker_ = tls_long_task_tracker->Get(); + + TRACE_EVENT_CATEGORY_GROUP_ENABLED("scheduler.long_tasks", &is_tracing_); + if (is_tracing_) { + task_start_time_ = tick_clock_->NowTicks(); + } + + tls_long_task_tracker->Set(this); +} + +TaskAnnotator::LongTaskTracker::~LongTaskTracker() { + auto* tls_long_task_tracker = GetTLSForCurrentLongTaskTracker(); + DCHECK_EQ(this, tls_long_task_tracker->Get()); + tls_long_task_tracker->Set(old_long_task_tracker_.get()); + + if (!is_tracing_) + return; + + TimeTicks task_end_time = tick_clock_->NowTicks(); + if ((task_end_time - task_start_time_) >= kMaxTaskDurationTimeDelta) { + TRACE_EVENT_BEGIN("scheduler.long_tasks", "LongTaskTracker", + perfetto::Track::ThreadScoped(task_annotator_), + task_start_time_, [&](perfetto::EventContext& ctx) { + TaskAnnotator::EmitTaskLocation(ctx, pending_task_); + EmitReceivedIPCDetails(ctx); + }); + TRACE_EVENT_END("scheduler.long_tasks", + perfetto::Track::ThreadScoped(task_annotator_), + task_end_time); + } +#if !BUILDFLAG(ENABLE_BASE_TRACING) + // Suppress the unused variable warning when TRACE_EVENT macros are turned + // into no-op. + (void)pending_task_; + (void)task_annotator_; +#endif // !BUILDFLAG(ENABLE_BASE_TRACING) +} + +void TaskAnnotator::LongTaskTracker::SetIpcDetails(const char* interface_name, + uint32_t (*method_info)(), + bool is_response) { + ipc_interface_name_ = interface_name; + is_response_ = is_response; + + if (!method_info) + return; + + ipc_hash_ = (*method_info)(); + ipc_method_info_ = method_info; +} + +void TaskAnnotator::LongTaskTracker::EmitReceivedIPCDetails( + perfetto::EventContext& ctx) { + if (!ipc_interface_name_ || !ipc_hash_ || !ipc_method_info_) + return; +#if BUILDFLAG(ENABLE_BASE_TRACING) && !BUILDFLAG(IS_NACL) + // Emit all of the IPC hash information if this task + // comes from a mojo interface. + auto* info = ctx.event()->set_chrome_mojo_event_info(); + info->set_mojo_interface_tag(ipc_interface_name_); + info->set_ipc_hash(ipc_hash_); + info->set_is_reply(is_response_); + + // The Native client will not build as the relevant implementation of + // base::ModuleCache::CreateModuleForAddress is not implemented for it. + // Thus the below code must be included on a conditional basis. + const auto ipc_method_address = reinterpret_cast<uintptr_t>(ipc_method_info_); + const absl::optional<size_t> location_iid = + base::trace_event::InternedUnsymbolizedSourceLocation::Get( + &ctx, ipc_method_address); + if (location_iid) { + info->set_mojo_interface_method_iid(*location_iid); + } +#endif +} + } // namespace base diff --git a/chromium/base/task/common/task_annotator.h b/chromium/base/task/common/task_annotator.h index 9d753e75f47..7fafe2463bb 100644 --- a/chromium/base/task/common/task_annotator.h +++ b/chromium/base/task/common/task_annotator.h @@ -11,10 +11,14 @@ #include "base/memory/raw_ptr.h" #include "base/pending_task.h" #include "base/strings/string_piece.h" +#include "base/time/tick_clock.h" #include "base/trace_event/base_tracing.h" namespace base { +// Constant used to measure which long-running tasks should be traced. +constexpr TimeDelta kMaxTaskDurationTimeDelta = Milliseconds(4); + // Implements common debug annotations for posted tasks. This includes data // such as task origins, IPC message contexts, queueing durations and memory // usage. @@ -31,8 +35,17 @@ class BASE_EXPORT TaskAnnotator { // to be used only from within generated IPC handler dispatch code. class ScopedSetIpcHash; + // This is used to track long-running browser-UI tasks. It is intended to + // be used for low-overhead logging to produce longer traces, particularly to + // help the scroll jank reduction effort. + class LongTaskTracker; + static const PendingTask* CurrentTaskForThread(); + static void OnIPCReceived(const char* interface_name, + uint32_t (*method_info)(), + bool is_response); + TaskAnnotator(); TaskAnnotator(const TaskAnnotator&) = delete; @@ -87,8 +100,8 @@ class BASE_EXPORT TaskAnnotator { #if BUILDFLAG(ENABLE_BASE_TRACING) // TRACE_EVENT argument helper, writing the task location data into // EventContext. - void EmitTaskLocation(perfetto::EventContext& ctx, - const PendingTask& task) const; + static void EmitTaskLocation(perfetto::EventContext& ctx, + const PendingTask& task); // TRACE_EVENT argument helper, writing the incoming task flow information // into EventContext if toplevel.flow category is enabled. @@ -125,6 +138,44 @@ class BASE_EXPORT TaskAnnotator::ScopedSetIpcHash { const char* ipc_interface_name_ = nullptr; }; +class BASE_EXPORT TaskAnnotator::LongTaskTracker { + public: + explicit LongTaskTracker(const TickClock* tick_clock, + PendingTask& pending_task, + TaskAnnotator* task_annotator); + + LongTaskTracker(const LongTaskTracker&) = delete; + + ~LongTaskTracker(); + + void SetIpcDetails(const char* interface_name, + uint32_t (*method_info)(), + bool is_response); + + private: + void EmitReceivedIPCDetails(perfetto::EventContext& ctx); + + // For tracking task duration + raw_ptr<const TickClock> tick_clock_; // Not owned. + TimeTicks task_start_time_; + + // Tracing variables. + + // Use this to ensure that tracing and NowTicks() are not called + // unnecessarily. + bool is_tracing_; + raw_ptr<LongTaskTracker> old_long_task_tracker_ = nullptr; + const char* ipc_interface_name_ = nullptr; + uint32_t ipc_hash_ = 0; + + // IPC method info to retrieve IPC hash and method address from trace, if + // known. Note that this will not compile in the Native client. + uint32_t (*ipc_method_info_)(); + bool is_response_ = false; + PendingTask& pending_task_; + raw_ptr<TaskAnnotator> task_annotator_; +}; + } // namespace base #endif // BASE_TASK_COMMON_TASK_ANNOTATOR_H_ diff --git a/chromium/base/task/common/task_annotator_unittest.cc b/chromium/base/task/common/task_annotator_unittest.cc index c4c5e59eb2e..5c71c3942bf 100644 --- a/chromium/base/task/common/task_annotator_unittest.cc +++ b/chromium/base/task/common/task_annotator_unittest.cc @@ -15,11 +15,11 @@ #include "base/strings/stringprintf.h" #include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" +#include "base/task/single_thread_task_runner.h" #include "base/task/thread_pool.h" #include "base/test/bind.h" #include "base/test/task_environment.h" #include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -174,7 +174,8 @@ TEST_F(TaskAnnotatorBacktraceIntegrationTest, SingleThreadedSimple) { // last 4 parents are kept). OnceClosure task5 = BindOnce( &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), ThreadTaskRunnerHandle::Get(), location5, FROM_HERE, + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), location5, + FROM_HERE, ExpectedTrace({location4.program_counter(), location3.program_counter(), location2.program_counter(), location1.program_counter()}), 0, run_loop.QuitClosure()); @@ -182,32 +183,36 @@ TEST_F(TaskAnnotatorBacktraceIntegrationTest, SingleThreadedSimple) { // Task i=4/3/2/1/0 have tasks [0,i) as parents. OnceClosure task4 = BindOnce( &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), ThreadTaskRunnerHandle::Get(), location4, location5, + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), location4, + location5, ExpectedTrace({location3.program_counter(), location2.program_counter(), location1.program_counter(), location0.program_counter()}), 0, std::move(task5)); OnceClosure task3 = BindOnce( &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), ThreadTaskRunnerHandle::Get(), location3, location4, + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), location3, + location4, ExpectedTrace({location2.program_counter(), location1.program_counter(), location0.program_counter()}), 0, std::move(task4)); OnceClosure task2 = BindOnce( &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), ThreadTaskRunnerHandle::Get(), location2, location3, + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), location2, + location3, ExpectedTrace({location1.program_counter(), location0.program_counter()}), dummy_ipc_hash, std::move(task3)); OnceClosure task1 = BindOnce( &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPostWithIpcContext, - Unretained(this), ThreadTaskRunnerHandle::Get(), location1, location2, - ExpectedTrace({location0.program_counter()}), 0, std::move(task2), - dummy_ipc_hash); + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), location1, + location2, ExpectedTrace({location0.program_counter()}), 0, + std::move(task2), dummy_ipc_hash); OnceClosure task0 = BindOnce(&TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), ThreadTaskRunnerHandle::Get(), location0, - location1, ExpectedTrace({}), 0, std::move(task1)); + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), + location0, location1, ExpectedTrace({}), 0, std::move(task1)); - ThreadTaskRunnerHandle::Get()->PostTask(location0, std::move(task0)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask(location0, + std::move(task0)); run_loop.Run(); } @@ -220,7 +225,7 @@ TEST_F(TaskAnnotatorBacktraceIntegrationTest, MultipleThreads) { // based SequencedTaskRunner, and a ThreadPool based // SingleThreadTaskRunner) to verify that TaskAnnotator can capture backtraces // for PostTasks back-and-forth between these. - auto main_thread_a = ThreadTaskRunnerHandle::Get(); + auto main_thread_a = SingleThreadTaskRunner::GetCurrentDefault(); auto task_runner_b = ThreadPool::CreateSingleThreadTaskRunner({}); auto task_runner_c = ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::WithBaseSyncPrimitives()}); @@ -359,19 +364,22 @@ TEST_F(TaskAnnotatorBacktraceIntegrationTest, SingleThreadedNested) { // 4. OnceClosure task5 = BindOnce( &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), ThreadTaskRunnerHandle::Get(), location5, FROM_HERE, + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), location5, + FROM_HERE, ExpectedTrace({location4.program_counter(), location3.program_counter(), location2.program_counter(), location1.program_counter()}), 0, run_loop.QuitClosure()); OnceClosure task4 = BindOnce( &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), ThreadTaskRunnerHandle::Get(), location4, location5, + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), location4, + location5, ExpectedTrace({location3.program_counter(), location2.program_counter(), location1.program_counter(), location0.program_counter()}), 0, std::move(task5)); OnceClosure task3 = BindOnce( &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), ThreadTaskRunnerHandle::Get(), location3, location4, + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), location3, + location4, ExpectedTrace({location2.program_counter(), location1.program_counter(), location0.program_counter()}), 0, std::move(task4)); @@ -382,7 +390,8 @@ TEST_F(TaskAnnotatorBacktraceIntegrationTest, SingleThreadedNested) { OnceClosure task2 = BindOnce( &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPost, - Unretained(this), ThreadTaskRunnerHandle::Get(), location2, location3, + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), location2, + location3, ExpectedTrace({location1.program_counter(), location0.program_counter()}), 0, std::move(run_task_3_then_quit_nested_loop1)); @@ -399,23 +408,26 @@ TEST_F(TaskAnnotatorBacktraceIntegrationTest, SingleThreadedNested) { // This context should not leak out of the inner loop and color the // tasks in the outer loop. TaskAnnotator::ScopedSetIpcHash scoped_ipc_hash(dummy_ipc_hash1); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, DoNothing()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, + DoNothing()); nested_run_loop->RunUntilIdle(); } - ThreadTaskRunnerHandle::Get()->PostTask(location2, std::move(task2)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask(location2, + std::move(task2)); }), Unretained(&nested_run_loop2), location2, std::move(task2)); OnceClosure task0 = BindOnce( &TaskAnnotatorBacktraceIntegrationTest::VerifyTraceAndPostWithIpcContext, - Unretained(this), ThreadTaskRunnerHandle::Get(), location0, location1, - ExpectedTrace({}), 0, std::move(task1), dummy_ipc_hash); + Unretained(this), SingleThreadTaskRunner::GetCurrentDefault(), location0, + location1, ExpectedTrace({}), 0, std::move(task1), dummy_ipc_hash); - ThreadTaskRunnerHandle::Get()->PostTask(location0, std::move(task0)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask(location0, + std::move(task0)); { TaskAnnotator::ScopedSetIpcHash scoped_ipc_hash(dummy_ipc_hash2); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&RunLoop::Run, Unretained(&nested_run_loop1), FROM_HERE)); } diff --git a/chromium/base/task/current_thread.cc b/chromium/base/task/current_thread.cc index 4382f7258fd..857b1dce241 100644 --- a/chromium/base/task/current_thread.cc +++ b/chromium/base/task/current_thread.cc @@ -107,7 +107,7 @@ CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop:: TRACE_EVENT_END0("base", "ScopedNestableTaskAllower"); } -bool CurrentThread::NestableTasksAllowed() const { +bool CurrentThread::ApplicationTasksAllowedInNativeNestedLoop() const { return current_->IsTaskExecutionAllowed(); } @@ -150,7 +150,7 @@ MessagePumpForUI* CurrentUIThread::GetMessagePumpForUI() const { return static_cast<MessagePumpForUI*>(current_->GetMessagePump()); } -#if defined(USE_OZONE) && !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_OZONE) && !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_WIN) bool CurrentUIThread::WatchFileDescriptor( int fd, bool persistent, diff --git a/chromium/base/task/current_thread.h b/chromium/base/task/current_thread.h index 0375681bdbc..9eb954fc91f 100644 --- a/chromium/base/task/current_thread.h +++ b/chromium/base/task/current_thread.h @@ -162,16 +162,10 @@ class BASE_EXPORT CurrentThread { const bool previous_state_; }; - // TODO(https://crbug.com/781352): Remove usage of this old class. Either - // renaming it to ScopedAllowApplicationTasksInNativeNestedLoop when truly - // native or migrating it to RunLoop::Type::kNestableTasksAllowed otherwise. - using ScopedNestableTaskAllower = - ScopedAllowApplicationTasksInNativeNestedLoop; - // Returns true if nestable tasks are allowed on the current thread at this - // time (i.e. if a nested loop would start from the callee's point in the - // stack, would it be allowed to run application tasks). - bool NestableTasksAllowed() const; + // time (i.e. if a native nested loop would start from the callee's point in + // the stack, would it be allowed to run application tasks). + bool ApplicationTasksAllowedInNativeNestedLoop() const; // Returns true if this instance is bound to the current thread. bool IsBoundToCurrentThread() const; @@ -218,7 +212,7 @@ class BASE_EXPORT CurrentUIThread : public CurrentThread { CurrentUIThread* operator->() { return this; } -#if defined(USE_OZONE) && !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_OZONE) && !BUILDFLAG(IS_FUCHSIA) && !BUILDFLAG(IS_WIN) static_assert( std::is_base_of<WatchableIOMessagePumpPosix, MessagePumpForUI>::value, "CurrentThreadForUI::WatchFileDescriptor is supported only" diff --git a/chromium/base/task/sequence_manager/associated_thread_id.cc b/chromium/base/task/sequence_manager/associated_thread_id.cc index 2db2f6f95fc..b4c7f24fa16 100644 --- a/chromium/base/task/sequence_manager/associated_thread_id.cc +++ b/chromium/base/task/sequence_manager/associated_thread_id.cc @@ -4,6 +4,9 @@ #include "base/task/sequence_manager/associated_thread_id.h" +#include "base/check.h" +#include "base/dcheck_is_on.h" + namespace base { namespace sequence_manager { namespace internal { @@ -12,13 +15,12 @@ AssociatedThreadId::AssociatedThreadId() = default; AssociatedThreadId::~AssociatedThreadId() = default; void AssociatedThreadId::BindToCurrentThread() { - // TODO(altimin): Remove this after MessageLoopImpl is gone and - // initialisation is simplified. - auto current_thread_id = PlatformThread::CurrentId(); - [[maybe_unused]] auto prev_thread_id = - thread_id_.exchange(current_thread_id, std::memory_order_release); - DCHECK(prev_thread_id == current_thread_id || - prev_thread_id == kInvalidThreadId); +#if DCHECK_IS_ON() + const auto prev_thread_ref = thread_ref_.load(std::memory_order_relaxed); + DCHECK(prev_thread_ref.is_null() || + prev_thread_ref == PlatformThread::CurrentRef()); +#endif + thread_ref_.store(PlatformThread::CurrentRef(), std::memory_order_release); // Rebind the thread and sequence checkers to the current thread/sequence. DETACH_FROM_THREAD(thread_checker); diff --git a/chromium/base/task/sequence_manager/associated_thread_id.h b/chromium/base/task/sequence_manager/associated_thread_id.h index 4379ed59552..befde1e2cd0 100644 --- a/chromium/base/task/sequence_manager/associated_thread_id.h +++ b/chromium/base/task/sequence_manager/associated_thread_id.h @@ -12,6 +12,7 @@ #include "base/memory/ref_counted.h" #include "base/sequence_checker.h" #include "base/threading/platform_thread.h" +#include "base/threading/platform_thread_ref.h" #include "base/threading/thread_checker.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -50,24 +51,6 @@ class BASE_EXPORT AssociatedThreadId // Can only be called once. void BindToCurrentThread(); - // Returns the id of the thread bound to this object via a previous call to - // BindToCurrentThread(), nullopt if no thread was bound yet. - // - // This method guarantees a happens-before ordering with - // BindToCurrentThread(), that is all memory writes that happened-before the - // call to BindToCurrentThread() will become visible side-effects in the - // current thread. - // - // Attention: The result might be stale by the time this method returns. - absl::optional<PlatformThreadId> GetBoundThreadId() const { - auto thread_id = thread_id_.load(std::memory_order_acquire); - if (thread_id == kInvalidThreadId) { - return absl::nullopt; - } else { - return thread_id; - } - } - // Checks whether this object has already been bound to a thread. // // This method guarantees a happens-before ordering with @@ -77,7 +60,7 @@ class BASE_EXPORT AssociatedThreadId // // Attention: The result might be stale by the time this method returns. bool IsBound() const { - return thread_id_.load(std::memory_order_acquire) != kInvalidThreadId; + return !thread_ref_.load(std::memory_order_acquire).is_null(); } // Checks whether this object is bound to the current thread. Returns false if @@ -90,20 +73,15 @@ class BASE_EXPORT AssociatedThreadId // // Attention:: The result might be stale by the time this method returns. bool IsBoundToCurrentThread() const { - return thread_id_.load(std::memory_order_relaxed) == - PlatformThread::CurrentId(); + return thread_ref_.load(std::memory_order_relaxed) == + PlatformThread::CurrentRef(); } - // TODO(eseckler): Add a method that checks that we are either bound already - // or on the thread which created us and use it in any_thread() accessors. - private: friend class base::RefCountedThreadSafe<AssociatedThreadId>; ~AssociatedThreadId(); - // All access to this member can be std::memory_order_relaxed as this class - // provides no ordering guarantees. - std::atomic<PlatformThreadId> thread_id_{kInvalidThreadId}; + std::atomic<PlatformThreadRef> thread_ref_{}; }; } // namespace internal diff --git a/chromium/base/task/sequence_manager/delayed_task_handle_delegate.h b/chromium/base/task/sequence_manager/delayed_task_handle_delegate.h index 81d8f6fe942..82e57b64533 100644 --- a/chromium/base/task/sequence_manager/delayed_task_handle_delegate.h +++ b/chromium/base/task/sequence_manager/delayed_task_handle_delegate.h @@ -42,7 +42,8 @@ class DelayedTaskHandleDelegate : public DelayedTaskHandle::Delegate { private: // The TaskQueueImpl where the task was posted. - const raw_ptr<TaskQueueImpl> outer_ GUARDED_BY_CONTEXT(sequence_checker_); + const raw_ptr<TaskQueueImpl, DanglingUntriaged> outer_ + GUARDED_BY_CONTEXT(sequence_checker_); // The HeapHandle to the task, if the task is in the DelayedIncomingQueue, // invalid otherwise. diff --git a/chromium/base/task/sequence_manager/sequence_manager.cc b/chromium/base/task/sequence_manager/sequence_manager.cc index 2af3bf0bcb3..ea9642aa8ed 100644 --- a/chromium/base/task/sequence_manager/sequence_manager.cc +++ b/chromium/base/task/sequence_manager/sequence_manager.cc @@ -7,8 +7,6 @@ namespace base { namespace sequence_manager { -NativeWorkHandle::~NativeWorkHandle() = default; - SequenceManager::MetricRecordingSettings::MetricRecordingSettings( double task_thread_time_sampling_rate) : task_sampling_rate_for_recording_cpu_time( diff --git a/chromium/base/task/sequence_manager/sequence_manager.h b/chromium/base/task/sequence_manager/sequence_manager.h index 3f1fffde130..ff61e8c7c2f 100644 --- a/chromium/base/task/sequence_manager/sequence_manager.h +++ b/chromium/base/task/sequence_manager/sequence_manager.h @@ -29,18 +29,6 @@ namespace sequence_manager { class TimeDomain; -// Represent outstanding work the sequence underlying a SequenceManager (e.g., -// a native system task for drawing the UI). As long as this handle is alive, -// the work is considered to be pending. -class NativeWorkHandle { - public: - virtual ~NativeWorkHandle(); - NativeWorkHandle(const NativeWorkHandle&) = delete; - - protected: - NativeWorkHandle() = default; -}; - // SequenceManager manages TaskQueues which have different properties // (e.g. priority, common task type) multiplexing all posted tasks into // a single backing sequence (currently bound to a single thread, which is @@ -95,7 +83,8 @@ class BASE_EXPORT SequenceManager { MessagePumpType message_loop_type = MessagePumpType::DEFAULT; bool randomised_sampling_enabled = false; - raw_ptr<const TickClock> clock = DefaultTickClock::GetInstance(); + raw_ptr<const TickClock, DanglingUntriaged> clock = + DefaultTickClock::GetInstance(); // If true, add the timestamp the task got queued to the task. bool add_queue_time_to_tasks = false; @@ -244,17 +233,6 @@ class BASE_EXPORT SequenceManager { // Returns a JSON string which describes all pending tasks. virtual std::string DescribeAllPendingTasks() const = 0; - // Indicates that the underlying sequence (e.g., the message pump) has pending - // work at priority `priority`. If the priority of the work in this - // SequenceManager is lower, it will yield to let the native work run. The - // native work is assumed to remain pending while the returned handle is - // valid. - // - // Must be called on the main thread, and the returned handle must also be - // deleted on the main thread. - virtual std::unique_ptr<NativeWorkHandle> OnNativeWorkPending( - TaskQueue::QueuePriority priority) = 0; - // While Now() is less than `prioritize_until` we will alternate between a // SequenceManager task and a yielding to the underlying sequence (e.g., the // message pump). diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl.cc b/chromium/base/task/sequence_manager/sequence_manager_impl.cc index f7d83a1eaa5..321c6099a96 100644 --- a/chromium/base/task/sequence_manager/sequence_manager_impl.cc +++ b/chromium/base/task/sequence_manager/sequence_manager_impl.cc @@ -118,6 +118,12 @@ std::unique_ptr<SequenceManager> CreateUnboundSequenceManager( namespace internal { +std::unique_ptr<SequenceManagerImpl> CreateUnboundSequenceManagerImpl( + PassKey<base::internal::SequenceManagerThreadDelegate>, + SequenceManager::Settings settings) { + return SequenceManagerImpl::CreateUnbound(std::move(settings)); +} + using TimeRecordingPolicy = base::sequence_manager::TaskQueue::TaskTiming::TimeRecordingPolicy; @@ -173,45 +179,13 @@ char* PrependHexAddress(char* output, const void* address) { // deciding when the next wake up should happen. // Note: An atomic is used here because some tests can initialize two different // sequence managers on different threads (e.g. by using base::Thread). -std::atomic_bool g_no_wake_ups_for_canceled_tasks{false}; +std::atomic_bool g_no_wake_ups_for_canceled_tasks{true}; -} // namespace - -class SequenceManagerImpl::NativeWorkHandleImpl final - : public NativeWorkHandle { - public: - NativeWorkHandleImpl(SequenceManagerImpl* sequence_manager, - TaskQueue::QueuePriority priority) - : sequence_manager_(sequence_manager->GetWeakPtr()), priority_(priority) { - TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("sequence_manager", "NativeWork", this, - "priority", - TaskQueue::PriorityToString(priority_)); - sequence_manager_->main_thread_only().pending_native_work.insert(priority_); - } +#if BUILDFLAG(IS_WIN) +bool g_explicit_high_resolution_timer_win = false; +#endif // BUILDFLAG(IS_WIN) - ~NativeWorkHandleImpl() final { - TRACE_EVENT_NESTABLE_ASYNC_END0("sequence_manager", "NativeWork", this); - if (!sequence_manager_) - return; - TaskQueue::QueuePriority prev_priority = effective_priority(); - sequence_manager_->main_thread_only().pending_native_work.erase(priority_); - // We should always have at least one instance of pending native work. By - // default it is of the lowest priority, which doesn't cause SequenceManager - // to yield. - DCHECK_GE(sequence_manager_->main_thread_only().pending_native_work.size(), - 1u); - if (prev_priority != effective_priority()) - sequence_manager_->ScheduleWork(); - } - - TaskQueue::QueuePriority effective_priority() const { - return *sequence_manager_->main_thread_only().pending_native_work.begin(); - } - - private: - WeakPtr<SequenceManagerImpl> sequence_manager_; - const TaskQueue::QueuePriority priority_; -}; +} // namespace // static SequenceManagerImpl* SequenceManagerImpl::GetCurrent() { @@ -339,10 +313,14 @@ std::unique_ptr<SequenceManagerImpl> SequenceManagerImpl::CreateUnbound( // static void SequenceManagerImpl::InitializeFeatures() { + base::InitializeTaskLeeway(); ApplyNoWakeUpsForCanceledTasks(); TaskQueueImpl::InitializeFeatures(); ThreadControllerWithMessagePumpImpl::InitializeFeatures(); - base::InitializeTaskLeeway(); +#if BUILDFLAG(IS_WIN) + g_explicit_high_resolution_timer_win = + FeatureList::IsEnabled(kExplicitHighResolutionTimerWin); +#endif // BUILDFLAG(IS_WIN) } // static @@ -689,13 +667,6 @@ SequenceManagerImpl::SelectNextTaskImpl(LazyNow& lazy_now, continue; } - if (UNLIKELY(!ShouldRunTaskOfPriority( - work_queue->task_queue()->GetQueuePriority()))) { - TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("sequence_manager"), - "SequenceManager.YieldToNative"); - return absl::nullopt; - } - #if DCHECK_IS_ON() && !BUILDFLAG(IS_NACL) LogTaskDebugInfo(work_queue); #endif // DCHECK_IS_ON() && !BUILDFLAG(IS_NACL) @@ -719,11 +690,6 @@ SequenceManagerImpl::SelectNextTaskImpl(LazyNow& lazy_now, } } -bool SequenceManagerImpl::ShouldRunTaskOfPriority( - TaskQueue::QueuePriority priority) const { - return priority <= *main_thread_only().pending_native_work.begin(); -} - void SequenceManagerImpl::DidRunTask(LazyNow& lazy_now) { ExecutingTask& executing_task = *main_thread_only().task_execution_stack.rbegin(); @@ -757,8 +723,6 @@ absl::optional<WakeUp> SequenceManagerImpl::GetPendingWakeUp( // If the selector has non-empty queues we trivially know there is immediate // work to be done. However we may want to yield to native work if it is // more important. - if (UNLIKELY(!ShouldRunTaskOfPriority(*priority))) - return AdjustWakeUp(GetNextDelayedWakeUpWithOption(option), lazy_now); return WakeUp{}; } @@ -769,8 +733,6 @@ absl::optional<WakeUp> SequenceManagerImpl::GetPendingWakeUp( if (auto priority = main_thread_only().selector.GetHighestPendingPriority(option)) { - if (UNLIKELY(!ShouldRunTaskOfPriority(*priority))) - return AdjustWakeUp(GetNextDelayedWakeUpWithOption(option), lazy_now); return WakeUp{}; } @@ -815,20 +777,38 @@ absl::optional<WakeUp> SequenceManagerImpl::AdjustWakeUp( void SequenceManagerImpl::MaybeAddLeewayToTask(Task& task) const { if (!main_thread_only().time_domain) { - task.leeway = base::GetTaskLeeway(); + task.leeway = GetTaskLeewayForCurrentThread(); } } +// TODO(crbug/1267874): Rename once ExplicitHighResolutionTimerWin experiment is +// shipped. bool SequenceManagerImpl::HasPendingHighResolutionTasks() { // Only consider high-res tasks in the |wake_up_queue| (ignore the // |non_waking_wake_up_queue|). +#if BUILDFLAG(IS_WIN) + if (g_explicit_high_resolution_timer_win) { + absl::optional<WakeUp> wake_up = + main_thread_only().wake_up_queue->GetNextDelayedWakeUp(); + if (!wake_up) + return false; + // Under the kExplicitHighResolutionTimerWin experiment, rely on leeway + // being larger than the minimum time of a low resolution timer (16ms). This + // way, we don't need to activate the high resolution timer for precise + // tasks that will run in more than 16ms if there are non precise tasks in + // front of them. + DCHECK_GE(GetDefaultTaskLeeway(), + Milliseconds(Time::kMinLowResolutionThresholdMs)); + return wake_up->delay_policy == subtle::DelayPolicy::kPrecise; + } +#endif // BUILDFLAG(IS_WIN) return main_thread_only().wake_up_queue->has_pending_high_resolution_tasks(); } bool SequenceManagerImpl::OnSystemIdle() { - auto wakeup = main_thread_only().wake_up_queue->GetNextDelayedWakeUp(); bool have_work_to_do = false; if (main_thread_only().time_domain) { + auto wakeup = main_thread_only().wake_up_queue->GetNextDelayedWakeUp(); have_work_to_do = main_thread_only().time_domain->MaybeFastForwardToWakeUp( wakeup, controller_->ShouldQuitRunLoopWhenIdle()); } @@ -1054,9 +1034,6 @@ Value::Dict SequenceManagerImpl::AsValueWithSelectorResult( state.Set("selected_queue", selected_work_queue->task_queue()->GetName()); state.Set("work_queue_name", selected_work_queue->name()); } - state.Set("native_work_priority", - TaskQueue::PriorityToString( - *main_thread_only().pending_native_work.begin())); state.Set("time_domain", main_thread_only().time_domain ? main_thread_only().time_domain->AsValue() : Value::Dict()); @@ -1201,11 +1178,6 @@ std::string SequenceManagerImpl::DescribeAllPendingTasks() const { return result; } -std::unique_ptr<NativeWorkHandle> SequenceManagerImpl::OnNativeWorkPending( - TaskQueue::QueuePriority priority) { - return std::make_unique<NativeWorkHandleImpl>(this, priority); -} - void SequenceManagerImpl::PrioritizeYieldingToNative( base::TimeTicks prioritize_until) { controller_->PrioritizeYieldingToNative(prioritize_until); diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl.h b/chromium/base/task/sequence_manager/sequence_manager_impl.h index 2e233cabcbb..19c8fd63e5b 100644 --- a/chromium/base/task/sequence_manager/sequence_manager_impl.h +++ b/chromium/base/task/sequence_manager/sequence_manager_impl.h @@ -40,12 +40,17 @@ #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_checker.h" #include "base/time/default_tick_clock.h" +#include "base/types/pass_key.h" #include "base/values.h" #include "build/build_config.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace base { +namespace internal { +class SequenceManagerThreadDelegate; +} + namespace trace_event { class ConvertableToTraceFormat; } // namespace trace_event @@ -61,8 +66,16 @@ namespace internal { class TaskQueueImpl; class DefaultWakeUpQueue; +class SequenceManagerImpl; class ThreadControllerImpl; +// A private factory method for SequenceManagerThreadDelegate which is +// equivalent to sequence_manager::CreateUnboundSequenceManager() but returns +// the underlying impl. +std::unique_ptr<SequenceManagerImpl> CreateUnboundSequenceManagerImpl( + PassKey<base::internal::SequenceManagerThreadDelegate>, + SequenceManager::Settings settings); + // The task queue manager provides N task queues and a selector interface for // choosing which task queue to service next. Each task queue consists of two // sub queues: @@ -87,20 +100,6 @@ class BASE_EXPORT SequenceManagerImpl SequenceManagerImpl& operator=(const SequenceManagerImpl&) = delete; ~SequenceManagerImpl() override; - // Assume direct control over current thread and create a SequenceManager. - // This function should be called only once per thread. - // This function assumes that a task execution environment is already - // initialized for the current thread. - static std::unique_ptr<SequenceManagerImpl> CreateOnCurrentThread( - SequenceManager::Settings settings = SequenceManager::Settings()); - - // Create an unbound SequenceManager (typically for a future thread). The - // SequenceManager can be initialized on the current thread and then needs to - // be bound and initialized on the target thread by calling one of the Bind*() - // methods. - static std::unique_ptr<SequenceManagerImpl> CreateUnbound( - SequenceManager::Settings settings); - // Initializes the state of all the sequence manager features. Must be invoked // after FeatureList initialization. static void InitializeFeatures(); @@ -141,8 +140,6 @@ class BASE_EXPORT SequenceManagerImpl scoped_refptr<TaskQueue> CreateTaskQueue( const TaskQueue::Spec& spec) override; std::string DescribeAllPendingTasks() const override; - std::unique_ptr<NativeWorkHandle> OnNativeWorkPending( - TaskQueue::QueuePriority priority) override; void PrioritizeYieldingToNative(base::TimeTicks prioritize_until) override; void EnablePeriodicYieldingToNative(base::TimeDelta interval) override; void AddTaskObserver(TaskObserver* task_observer) override; @@ -223,14 +220,41 @@ class BASE_EXPORT SequenceManagerImpl friend class ::base::sequence_manager::SequenceManagerForTest; private: - class NativeWorkHandleImpl; - // Returns the SequenceManager running the // current thread. It must only be used on the thread it was obtained. // Only to be used by CurrentThread for the moment static SequenceManagerImpl* GetCurrent(); friend class ::base::CurrentThread; + // Factory friends to call into private creation methods. + friend std::unique_ptr<SequenceManager> + sequence_manager::CreateSequenceManagerOnCurrentThread( + SequenceManager::Settings); + friend std::unique_ptr<SequenceManager> + sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump( + std::unique_ptr<MessagePump> message_pump, + SequenceManager::Settings); + friend std::unique_ptr<SequenceManager> + sequence_manager::CreateUnboundSequenceManager(SequenceManager::Settings); + friend std::unique_ptr<SequenceManagerImpl> + sequence_manager::internal::CreateUnboundSequenceManagerImpl( + PassKey<base::internal::SequenceManagerThreadDelegate>, + SequenceManager::Settings); + + // Assume direct control over current thread and create a SequenceManager. + // This function should be called only once per thread. + // This function assumes that a task execution environment is already + // initialized for the current thread. + static std::unique_ptr<SequenceManagerImpl> CreateOnCurrentThread( + SequenceManager::Settings settings); + + // Create an unbound SequenceManager (typically for a future thread). The + // SequenceManager can be initialized on the current thread and then needs to + // be bound and initialized on the target thread by calling one of the Bind*() + // methods. + static std::unique_ptr<SequenceManagerImpl> CreateUnbound( + SequenceManager::Settings settings); + enum class ProcessTaskResult { kDeferred, kExecuted, @@ -340,10 +364,6 @@ class BASE_EXPORT SequenceManagerImpl // If non-null, invoked the next time OnSystemIdle() completes without // scheduling additional work. OnceClosure on_next_idle_callback; - - // By default native work is not prioritized at all. - std::multiset<TaskQueue::QueuePriority> pending_native_work{ - TaskQueue::kBestEffortPriority}; }; void CompleteInitializationOnBoundThread(); @@ -422,10 +442,6 @@ class BASE_EXPORT SequenceManagerImpl absl::optional<SelectedTask> SelectNextTaskImpl(LazyNow& lazy_now, SelectTaskOption option); - // Check if a task of priority |priority| should run given the pending set of - // native work. - bool ShouldRunTaskOfPriority(TaskQueue::QueuePriority priority) const; - // Returns a wake-up for the next delayed task which is not ripe for // execution, or nullopt if `option` is `kSkipDelayedTask` or there // are no such tasks (immediate tasks don't count). diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc index e2e29f9d696..635dcd6d840 100644 --- a/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc +++ b/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc @@ -54,7 +54,6 @@ #include "base/test/test_simple_task_runner.h" #include "base/threading/sequence_local_storage_slot.h" #include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "base/trace_event/base_tracing.h" #include "base/tracing_buildflags.h" @@ -182,7 +181,7 @@ class FixtureWithMockTaskRunner final : public Fixture { test_task_runner_)), sequence_manager_(SequenceManagerForTest::Create( nullptr, - ThreadTaskRunnerHandle::Get(), + SingleThreadTaskRunner::GetCurrentDefault(), mock_tick_clock(), SequenceManager::Settings::Builder() .SetMessagePumpType(MessagePumpType::DEFAULT) @@ -339,8 +338,7 @@ class FixtureWithMockMessagePump : public Fixture { TimeTicks FromStartAligned(TimeDelta delta) const override { if (wake_up_type_ == WakeUpType::kAlign) { - return (start_time_ + delta) - .SnappedToNextTick(TimeTicks(), Milliseconds(8)); + return (start_time_ + delta).SnappedToNextTick(TimeTicks(), kLeeway); } return start_time_ + delta; } @@ -371,8 +369,6 @@ class SequenceManagerTest fixture_ = std::make_unique<FixtureWithMockMessagePump>(GetWakeUpType()); break; - default: - NOTREACHED(); } } @@ -452,7 +448,9 @@ class SequenceManagerTest auto GetTestTypes() { return testing::Values( std::make_tuple(RunnerType::kMessagePump, WakeUpType::kDefault), +#if !BUILDFLAG(IS_WIN) std::make_tuple(RunnerType::kMessagePump, WakeUpType::kAlign), +#endif std::make_tuple(RunnerType::kMockTaskRunner, WakeUpType::kDefault)); } @@ -920,7 +918,7 @@ TEST_P(SequenceManagerTest, DelayedTaskAtPosting_FlexiblePreferEarly) { TimeTicks start_time = sequence_manager()->NowTicks(); std::vector<EnqueueOrder> run_order; - constexpr TimeDelta kDelay(Milliseconds(10)); + constexpr TimeDelta kDelay(Milliseconds(20)); auto handle = queue->task_runner()->PostCancelableDelayedTaskAt( subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE, BindOnce(&TestTask, 1, &run_order), @@ -2427,21 +2425,21 @@ TEST_P(SequenceManagerTest, sequence_manager()->SetTimeDomain(domain.get()); queue->task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&TestTask, 1, &run_order), Milliseconds(40)); + FROM_HERE, BindOnce(&TestTask, 1, &run_order), Milliseconds(400)); sequence_manager()->ResetTimeDomain(); queue->task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&TestTask, 2, &run_order), Milliseconds(30)); + FROM_HERE, BindOnce(&TestTask, 2, &run_order), Milliseconds(300)); sequence_manager()->SetTimeDomain(domain.get()); queue->task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&TestTask, 3, &run_order), Milliseconds(20)); + FROM_HERE, BindOnce(&TestTask, 3, &run_order), Milliseconds(200)); sequence_manager()->ResetTimeDomain(); queue->task_runner()->PostDelayedTask( - FROM_HERE, BindOnce(&TestTask, 4, &run_order), Milliseconds(10)); + FROM_HERE, BindOnce(&TestTask, 4, &run_order), Milliseconds(100)); - FastForwardBy(Milliseconds(40)); + FastForwardBy(Milliseconds(400)); EXPECT_THAT(run_order, ElementsAre(4u, 3u, 2u, 1u)); queue->ShutdownTaskQueue(); @@ -4303,7 +4301,7 @@ TEST(SequenceManagerBasicTest, DefaultTaskRunnerSupport) { base_sequence_manager->SetDefaultTaskRunner(queue->task_runner()); scoped_refptr<SingleThreadTaskRunner> original_task_runner = - ThreadTaskRunnerHandle::Get(); + SingleThreadTaskRunner::GetCurrentDefault(); scoped_refptr<SingleThreadTaskRunner> custom_task_runner = MakeRefCounted<TestSimpleTaskRunner>(); { @@ -4311,9 +4309,9 @@ TEST(SequenceManagerBasicTest, DefaultTaskRunnerSupport) { CreateSequenceManagerOnCurrentThread(SequenceManager::Settings()); manager->SetDefaultTaskRunner(custom_task_runner); - DCHECK_EQ(custom_task_runner, ThreadTaskRunnerHandle::Get()); + DCHECK_EQ(custom_task_runner, SingleThreadTaskRunner::GetCurrentDefault()); } - DCHECK_EQ(original_task_runner, ThreadTaskRunnerHandle::Get()); + DCHECK_EQ(original_task_runner, SingleThreadTaskRunner::GetCurrentDefault()); } TEST_P(SequenceManagerTest, CanceledTasksInQueueCantMakeOtherTasksSkipAhead) { @@ -4501,8 +4499,8 @@ TEST_P(SequenceManagerTest, DestructorPostsViaTaskRunnerHandleDuringShutdown) { bool run = false; task_queue->task_runner()->PostTask( FROM_HERE, RunOnDestruction(BindLambdaForTesting([&]() { - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::BindOnce(&NopTask)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(&NopTask)); run = true; }))); @@ -5185,56 +5183,6 @@ TEST_P(SequenceManagerTest, ReclaimMemoryRemovesCorrectQueueFromSet) { EXPECT_THAT(order, ElementsAre(1, 2, 3)); } -TEST_P(SequenceManagerTest, OnNativeWorkPending) { - MockTask task; - auto queue = CreateTaskQueue(); - queue->SetQueuePriority(TaskQueue::QueuePriority::kNormalPriority); - - auto CheckPostedTaskRan = [&](bool should_have_run) { - EXPECT_CALL(task, Run).Times(should_have_run ? 1 : 0); - RunLoop().RunUntilIdle(); - Mock::VerifyAndClearExpectations(&task); - }; - - // Scheduling native work with higher priority causes the posted task to be - // deferred. - auto native_work = sequence_manager()->OnNativeWorkPending( - TaskQueue::QueuePriority::kHighPriority); - queue->task_runner()->PostTask(FROM_HERE, task.Get()); - CheckPostedTaskRan(false); - - // Once the native work completes, the posted task is free to execute. - native_work.reset(); - CheckPostedTaskRan(true); - - // Lower priority native work doesn't preempt posted tasks. - native_work = sequence_manager()->OnNativeWorkPending( - TaskQueue::QueuePriority::kLowPriority); - queue->task_runner()->PostTask(FROM_HERE, task.Get()); - CheckPostedTaskRan(true); - - // Equal priority native work doesn't preempt posted tasks. - native_work = sequence_manager()->OnNativeWorkPending( - TaskQueue::QueuePriority::kNormalPriority); - queue->task_runner()->PostTask(FROM_HERE, task.Get()); - CheckPostedTaskRan(true); - - // When there are multiple priorities of native work, only the highest - // priority matters. - native_work = sequence_manager()->OnNativeWorkPending( - TaskQueue::QueuePriority::kNormalPriority); - auto native_work_high = sequence_manager()->OnNativeWorkPending( - TaskQueue::QueuePriority::kHighPriority); - auto native_work_low = sequence_manager()->OnNativeWorkPending( - TaskQueue::QueuePriority::kLowPriority); - queue->task_runner()->PostTask(FROM_HERE, task.Get()); - CheckPostedTaskRan(false); - native_work.reset(); - CheckPostedTaskRan(false); - native_work_high.reset(); - CheckPostedTaskRan(true); -} - namespace { class TaskObserverExpectingNoDelayedRunTime : public TaskObserver { @@ -5534,11 +5482,12 @@ TEST(SequenceManagerTest, sequence_manager->SetDefaultTaskRunner(queue->task_runner()); scoped_refptr<SingleThreadTaskRunner> expected_task_runner = - ThreadTaskRunnerHandle::Get(); + SingleThreadTaskRunner::GetCurrentDefault(); StrictMock<MockCallback<base::OnceCallback<void()>>> cb; EXPECT_CALL(cb, Run).WillOnce(testing::Invoke([expected_task_runner]() { - EXPECT_EQ(ThreadTaskRunnerHandle::Get(), expected_task_runner); + EXPECT_EQ(SingleThreadTaskRunner::GetCurrentDefault(), + expected_task_runner); })); static base::SequenceLocalStorageSlot<std::unique_ptr<DestructionCallback>> diff --git a/chromium/base/task/sequence_manager/sequenced_task_source.h b/chromium/base/task/sequence_manager/sequenced_task_source.h index e89837e82ce..0b33f7a1973 100644 --- a/chromium/base/task/sequence_manager/sequenced_task_source.h +++ b/chromium/base/task/sequence_manager/sequenced_task_source.h @@ -7,6 +7,7 @@ #include "base/base_export.h" #include "base/callback_helpers.h" +#include "base/memory/raw_ref.h" #include "base/pending_task.h" #include "base/task/common/lazy_now.h" #include "base/task/sequence_manager/task_queue.h" @@ -37,7 +38,7 @@ class SequencedTaskSource { QueueName task_queue_name); ~SelectedTask(); - Task& task; + const raw_ref<Task> task; // Callback to fill trace event arguments associated with the task // execution. Can be null TaskExecutionTraceLogger task_execution_trace_logger = diff --git a/chromium/base/task/sequence_manager/task_queue_impl.cc b/chromium/base/task/sequence_manager/task_queue_impl.cc index bd5b73bffb7..fe62bb9d147 100644 --- a/chromium/base/task/sequence_manager/task_queue_impl.cc +++ b/chromium/base/task/sequence_manager/task_queue_impl.cc @@ -80,7 +80,7 @@ namespace { // kSweepCancelledTasks and kExplicitHighResolutionTimerWin features. This // avoids the need to constantly query their enabled state through // FeatureList::IsEnabled(). -bool g_is_remove_canceled_tasks_in_task_queue_enabled = false; +bool g_is_remove_canceled_tasks_in_task_queue_enabled = true; bool g_is_sweep_cancelled_tasks_enabled = kSweepCancelledTasks.default_state == FEATURE_ENABLED_BY_DEFAULT; #if BUILDFLAG(IS_WIN) @@ -1110,13 +1110,8 @@ Task TaskQueueImpl::MakeDelayedTask(PostedTask delayed_task, delay = absl::get<base::TimeTicks>(delayed_task.delay_or_delayed_run_time) - lazy_now->Now(); } - if (explicit_high_resolution_timer_win) { - resolution = - delayed_task.delay_policy == base::subtle::DelayPolicy::kPrecise - ? WakeUpResolution::kHigh - : WakeUpResolution::kLow; - } else if (delay < - (2 * base::Milliseconds(Time::kMinLowResolutionThresholdMs))) { + if (!explicit_high_resolution_timer_win && + delay < (2 * base::Milliseconds(Time::kMinLowResolutionThresholdMs))) { // Outside the kExplicitHighResolutionTimerWin experiment, We consider the // task needs a high resolution timer if the delay is more than 0 and less // than 32ms. This caps the relative error to less than 50% : a 33ms wait diff --git a/chromium/base/task/sequence_manager/task_queue_selector.h b/chromium/base/task/sequence_manager/task_queue_selector.h index 53c1473b1ae..7b57b3d55a8 100644 --- a/chromium/base/task/sequence_manager/task_queue_selector.h +++ b/chromium/base/task/sequence_manager/task_queue_selector.h @@ -16,7 +16,6 @@ #include "base/task/sequence_manager/sequence_manager.h" #include "base/task/sequence_manager/sequenced_task_source.h" #include "base/task/sequence_manager/task_order.h" -#include "base/task/sequence_manager/task_queue_selector_logic.h" #include "base/task/sequence_manager/work_queue_sets.h" #include "base/values.h" #include "third_party/abseil-cpp/absl/types/optional.h" diff --git a/chromium/base/task/sequence_manager/task_queue_selector_logic.h b/chromium/base/task/sequence_manager/task_queue_selector_logic.h deleted file mode 100644 index 46e62d195d6..00000000000 --- a/chromium/base/task/sequence_manager/task_queue_selector_logic.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2015 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_LOGIC_H_ -#define BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_LOGIC_H_ - -namespace base { -namespace sequence_manager { -namespace internal { - -// Used to describe the logic trigerred when a task queue is selected to -// service. -// This enum is used for histograms and should not be renumbered. -enum class TaskQueueSelectorLogic { - // Selected due to priority rules. - kControlPriorityLogic = 0, - kHighestPriorityLogic = 1, - kHighPriorityLogic = 2, - kNormalPriorityLogic = 3, - kLowPriorityLogic = 4, - kBestEffortPriorityLogic = 5, - - // Selected due to starvation logic. - kHighPriorityStarvationLogic = 6, - kNormalPriorityStarvationLogic = 7, - kLowPriorityStarvationLogic = 8, - - kCount = 9, -}; - -} // namespace internal -} // namespace sequence_manager -} // namespace base - -#endif // BASE_TASK_SEQUENCE_MANAGER_TASK_QUEUE_SELECTOR_LOGIC_H_ diff --git a/chromium/base/task/sequence_manager/thread_controller.cc b/chromium/base/task/sequence_manager/thread_controller.cc index cd82149ffe2..b2719dd8b4b 100644 --- a/chromium/base/task/sequence_manager/thread_controller.cc +++ b/chromium/base/task/sequence_manager/thread_controller.cc @@ -32,7 +32,7 @@ ThreadController::RunLevelTracker::RunLevelTracker( : outer_(outer) {} ThreadController::RunLevelTracker::~RunLevelTracker() { - DCHECK_CALLED_ON_VALID_THREAD(outer_.associated_thread_->thread_checker); + DCHECK_CALLED_ON_VALID_THREAD(outer_->associated_thread_->thread_checker); // There shouldn't be any remaining |run_levels_| by the time this unwinds. DCHECK_EQ(run_levels_.size(), 0u); @@ -57,7 +57,7 @@ void ThreadController::RunLevelTracker::TimeKeeper::EnableRecording( DCHECK(!histogram_); histogram_ = LinearHistogram::FactoryGet( JoinString({"Scheduling.MessagePumpTimeKeeper", thread_name}, "."), 1, - Phase::kMaxValue, Phase::kMaxValue + 1, + Phase::kLastPhase, Phase::kLastPhase + 1, base::HistogramBase::kUmaTargetedHistogramFlag); #if BUILDFLAG(ENABLE_BASE_TRACING) @@ -73,7 +73,7 @@ void ThreadController::RunLevelTracker::TimeKeeper::EnableRecording( void ThreadController::RunLevelTracker::OnRunLoopStarted(State initial_state, LazyNow& lazy_now) { - DCHECK_CALLED_ON_VALID_THREAD(outer_.associated_thread_->thread_checker); + DCHECK_CALLED_ON_VALID_THREAD(outer_->associated_thread_->thread_checker); const bool is_nested = !run_levels_.empty(); run_levels_.emplace(initial_state, is_nested, time_keeper_, lazy_now); @@ -84,7 +84,7 @@ void ThreadController::RunLevelTracker::OnRunLoopStarted(State initial_state, } void ThreadController::RunLevelTracker::OnRunLoopEnded() { - DCHECK_CALLED_ON_VALID_THREAD(outer_.associated_thread_->thread_checker); + DCHECK_CALLED_ON_VALID_THREAD(outer_->associated_thread_->thread_checker); // Normally this will occur while kIdle or kInBetweenWorkItems but it can also // occur while kRunningWorkItem in rare situations where the owning // ThreadController is deleted from within a task. Ref. @@ -92,13 +92,13 @@ void ThreadController::RunLevelTracker::OnRunLoopEnded() { // can't assert anything about the current state other than that it must be // exiting an existing RunLevel. DCHECK(!run_levels_.empty()); - LazyNow exit_lazy_now(outer_.time_source_); + LazyNow exit_lazy_now(outer_->time_source_); run_levels_.top().set_exit_lazy_now(&exit_lazy_now); run_levels_.pop(); } void ThreadController::RunLevelTracker::OnWorkStarted(LazyNow& lazy_now) { - DCHECK_CALLED_ON_VALID_THREAD(outer_.associated_thread_->thread_checker); + DCHECK_CALLED_ON_VALID_THREAD(outer_->associated_thread_->thread_checker); // Ignore work outside the main run loop. // The only practical case where this would happen is if a native loop is spun // outside the main runloop (e.g. system dialog during startup). We cannot @@ -128,7 +128,7 @@ void ThreadController::RunLevelTracker::OnWorkStarted(LazyNow& lazy_now) { void ThreadController::RunLevelTracker::OnApplicationTaskSelected( TimeTicks queue_time, LazyNow& lazy_now) { - DCHECK_CALLED_ON_VALID_THREAD(outer_.associated_thread_->thread_checker); + DCHECK_CALLED_ON_VALID_THREAD(outer_->associated_thread_->thread_checker); // As-in OnWorkStarted. Early native loops can result in // ThreadController::DoWork because the lack of a top-level RunLoop means // `task_execution_allowed` wasn't consumed. @@ -142,7 +142,7 @@ void ThreadController::RunLevelTracker::OnApplicationTaskSelected( } void ThreadController::RunLevelTracker::OnWorkEnded(LazyNow& lazy_now) { - DCHECK_CALLED_ON_VALID_THREAD(outer_.associated_thread_->thread_checker); + DCHECK_CALLED_ON_VALID_THREAD(outer_->associated_thread_->thread_checker); if (run_levels_.empty()) return; @@ -161,7 +161,7 @@ void ThreadController::RunLevelTracker::OnWorkEnded(LazyNow& lazy_now) { } void ThreadController::RunLevelTracker::OnIdle(LazyNow& lazy_now) { - DCHECK_CALLED_ON_VALID_THREAD(outer_.associated_thread_->thread_checker); + DCHECK_CALLED_ON_VALID_THREAD(outer_->associated_thread_->thread_checker); if (run_levels_.empty()) return; @@ -370,7 +370,7 @@ void ThreadController::RunLevelTracker::TimeKeeper::RecordEndOfPhase( bool ThreadController::RunLevelTracker::TimeKeeper::ShouldRecordNow( ShouldRecordReqs reqs) { DCHECK_CALLED_ON_VALID_THREAD( - outer_.outer_.associated_thread_->thread_checker); + outer_->outer_->associated_thread_->thread_checker); // Recording is technically enabled once `histogram_` is set, however // `last_phase_end_` will be null until the next RecordWakeUp in the work // cycle in which `histogram_` is enabled. Only start recording from there. @@ -382,12 +382,12 @@ bool ThreadController::RunLevelTracker::TimeKeeper::ShouldRecordNow( switch (reqs) { case ShouldRecordReqs::kRegular: return histogram_ && !last_phase_end_.is_null() && - outer_.run_levels_.size() == 1; + outer_->run_levels_.size() == 1; case ShouldRecordReqs::kOnWakeUp: - return histogram_ && outer_.run_levels_.size() == 1; + return histogram_ && outer_->run_levels_.size() == 1; case ShouldRecordReqs::kOnEndNested: return histogram_ && !last_phase_end_.is_null() && - outer_.run_levels_.size() <= 2; + outer_->run_levels_.size() <= 2; } } @@ -419,8 +419,8 @@ void ThreadController::RunLevelTracker::TimeKeeper::RecordTimeInPhase( if (phase == kIdleWork) last_sleep_ = phase_end; - if (outer_.trace_observer_for_testing_) - outer_.trace_observer_for_testing_->OnPhaseRecorded(phase); + if (outer_->trace_observer_for_testing_) + outer_->trace_observer_for_testing_->OnPhaseRecorded(phase); } // static diff --git a/chromium/base/task/sequence_manager/thread_controller.h b/chromium/base/task/sequence_manager/thread_controller.h index aa12d8feda3..bab2236356e 100644 --- a/chromium/base/task/sequence_manager/thread_controller.h +++ b/chromium/base/task/sequence_manager/thread_controller.h @@ -10,6 +10,7 @@ #include "base/base_export.h" #include "base/check.h" +#include "base/memory/raw_ptr.h" #include "base/memory/raw_ref.h" #include "base/memory/scoped_refptr.h" #include "base/message_loop/message_pump.h" @@ -64,10 +65,10 @@ class BASE_EXPORT ThreadController { kApplicationTask = 5, kIdleWork = 6, kNested = 7, + kLastPhase = kNested, // Reported as a kWorkItem but doesn't clear state relevant to the ongoing // work item as it isn't finished (will resume after nesting). - kWorkItemSuspendedOnNested = 8, - kMaxValue = kNested + kWorkItemSuspendedOnNested, }; explicit ThreadController(const TickClock* time_source); @@ -267,7 +268,7 @@ class BASE_EXPORT ThreadController { void OnIdle(LazyNow& lazy_now); size_t num_run_levels() const { - DCHECK_CALLED_ON_VALID_THREAD(outer_.associated_thread_->thread_checker); + DCHECK_CALLED_ON_VALID_THREAD(outer_->associated_thread_->thread_checker); return run_levels_.size(); } @@ -340,7 +341,7 @@ class BASE_EXPORT ThreadController { static const char* PhaseToEventName(Phase phase); // Cumulative time deltas for each phase, reported and reset when >=100ms. - std::array<TimeDelta, Phase::kMaxValue + 1> deltas_ = {}; + std::array<TimeDelta, Phase::kLastPhase + 1> deltas_ = {}; // Set at the start of the first work item out-of-idle. Consumed from the // first application task found in that work cycle // (in OnApplicationTaskSelected). @@ -356,14 +357,14 @@ class BASE_EXPORT ThreadController { bool current_work_item_is_native_ = true; // non-null when recording is enabled. - HistogramBase* histogram_ = nullptr; + raw_ptr<HistogramBase> histogram_ = nullptr; #if BUILDFLAG(ENABLE_BASE_TRACING) absl::optional<perfetto::Track> perfetto_track_; // True if tracing was enabled during the last pass of RecordTimeInPhase. bool was_tracing_enabled_; #endif - const RunLevelTracker& outer_; + const raw_ref<const RunLevelTracker> outer_; } time_keeper_{*this}; class RunLevel { @@ -396,7 +397,7 @@ class BASE_EXPORT ThreadController { const raw_ref<TimeKeeper> time_keeper_; // Must be set shortly before ~RunLevel. - LazyNow* exit_lazy_now_ = nullptr; + raw_ptr<LazyNow> exit_lazy_now_ = nullptr; SampleMetadata thread_controller_sample_metadata_; size_t thread_controller_active_id_ = 0; @@ -418,10 +419,10 @@ class BASE_EXPORT ThreadController { TruePostMove was_moved_; }; - [[maybe_unused]] const ThreadController& outer_; + [[maybe_unused]] const raw_ref<const ThreadController> outer_; std::stack<RunLevel, std::vector<RunLevel>> run_levels_ - GUARDED_BY_CONTEXT(outer_.associated_thread_->thread_checker); + GUARDED_BY_CONTEXT(outer_->associated_thread_->thread_checker); static TraceObserverForTesting* trace_observer_for_testing_; } run_level_tracker_{*this}; diff --git a/chromium/base/task/sequence_manager/thread_controller_impl.cc b/chromium/base/task/sequence_manager/thread_controller_impl.cc index 9e56b5f3664..91f3b523d73 100644 --- a/chromium/base/task/sequence_manager/thread_controller_impl.cc +++ b/chromium/base/task/sequence_manager/thread_controller_impl.cc @@ -191,8 +191,8 @@ void ThreadControllerImpl::DoWork(WorkType work_type) { sequence_->SelectNextTask(lazy_now_select_task); LazyNow lazy_now_task_selected(time_source_); run_level_tracker_.OnApplicationTaskSelected( - (selected_task && selected_task->task.delayed_run_time.is_null()) - ? selected_task->task.queue_time + (selected_task && selected_task->task->delayed_run_time.is_null()) + ? selected_task->task->queue_time : TimeTicks(), lazy_now_task_selected); if (!selected_task) { @@ -209,11 +209,11 @@ void ThreadControllerImpl::DoWork(WorkType work_type) { // Note: all arguments after task are just passed to a TRACE_EVENT for // logging so lambda captures are safe as lambda is executed inline. task_annotator_.RunTask( - "ThreadControllerImpl::RunTask", selected_task->task, + "ThreadControllerImpl::RunTask", *selected_task->task, [&selected_task](perfetto::EventContext& ctx) { if (selected_task->task_execution_trace_logger) selected_task->task_execution_trace_logger.Run( - ctx, selected_task->task); + ctx, *selected_task->task); SequenceManagerImpl::MaybeEmitTaskDetails(ctx, *selected_task); }); if (!weak_ptr) diff --git a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc index 55d96758cb1..51a026dea28 100644 --- a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc +++ b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc @@ -12,9 +12,12 @@ #include "base/feature_list.h" #include "base/logging.h" #include "base/memory/ptr_util.h" +#include "base/memory/raw_ref.h" #include "base/message_loop/message_pump.h" #include "base/metrics/histogram.h" #include "base/metrics/histogram_macros.h" +#include "base/task/common/task_annotator.h" +#include "base/task/sequence_manager/sequence_manager_impl.h" #include "base/task/sequence_manager/tasks.h" #include "base/task/task_features.h" #include "base/threading/hang_watcher.h" @@ -51,15 +54,34 @@ BASE_FEATURE(kRunTasksByBatches, "RunTasksByBatches", base::FEATURE_DISABLED_BY_DEFAULT); +#if BUILDFLAG(IS_WIN) +// If enabled, deactivate the high resolution timer immediately in DoWork(), +// instead of waiting for next DoIdleWork. +BASE_FEATURE(kUseLessHighResTimers, + "UseLessHighResTimers", + base::FEATURE_DISABLED_BY_DEFAULT); +std::atomic_bool g_use_less_high_res_timers = false; +#endif + std::atomic_bool g_align_wake_ups = false; std::atomic_bool g_run_tasks_by_batches = false; +#if BUILDFLAG(IS_WIN) +bool g_explicit_high_resolution_timer_win = false; +#endif // BUILDFLAG(IS_WIN) TimeTicks WakeUpRunTime(const WakeUp& wake_up) { + // Windows relies on the low resolution timer rather than manual wake up + // alignment. +#if BUILDFLAG(IS_WIN) + if (g_explicit_high_resolution_timer_win) + return wake_up.earliest_time(); +#else // BUILDFLAG(IS_WIN) if (g_align_wake_ups.load(std::memory_order_relaxed)) { TimeTicks aligned_run_time = wake_up.earliest_time().SnappedToNextTick( - TimeTicks(), base::GetTaskLeeway()); + TimeTicks(), GetTaskLeewayForCurrentThread()); return std::min(aligned_run_time, wake_up.latest_time()); } +#endif return wake_up.time; } @@ -70,6 +92,12 @@ void ThreadControllerWithMessagePumpImpl::InitializeFeatures() { g_align_wake_ups = FeatureList::IsEnabled(kAlignWakeUps); g_run_tasks_by_batches.store(FeatureList::IsEnabled(kRunTasksByBatches), std::memory_order_relaxed); +#if BUILDFLAG(IS_WIN) + g_explicit_high_resolution_timer_win = + FeatureList::IsEnabled(kExplicitHighResolutionTimerWin); + g_use_less_high_res_timers.store( + FeatureList::IsEnabled(kUseLessHighResTimers), std::memory_order_relaxed); +#endif } // static @@ -219,7 +247,8 @@ void ThreadControllerWithMessagePumpImpl::InitializeThreadTaskRunnerHandle() { // so reset the old one. main_thread_only().thread_task_runner_handle.reset(); main_thread_only().thread_task_runner_handle = - std::make_unique<ThreadTaskRunnerHandle>(task_runner_); + std::make_unique<SingleThreadTaskRunner::CurrentDefaultHandle>( + task_runner_); // When the task runner is known, bind the power manager. Power notifications // are received through that sequence. power_monitor_.BindToCurrentThread(); @@ -290,6 +319,15 @@ void ThreadControllerWithMessagePumpImpl::BeforeWait() { MessagePump::Delegate::NextWorkInfo ThreadControllerWithMessagePumpImpl::DoWork() { +#if BUILDFLAG(IS_WIN) + // We've been already in a wakeup here. Deactivate the high res timer of OS + // immediately instead of waiting for next DoIdleWork(). + if (g_use_less_high_res_timers.load(std::memory_order_relaxed) && + main_thread_only().in_high_res_mode) { + main_thread_only().in_high_res_mode = false; + Time::ActivateHighResolutionTimer(false); + } +#endif MessagePump::Delegate::NextWorkInfo next_work_info{}; work_deduplicator_.OnWorkStarted(); @@ -418,8 +456,8 @@ WorkDetails ThreadControllerWithMessagePumpImpl::DoWorkImpl( select_task_option); LazyNow lazy_now_task_selected(time_source_); run_level_tracker_.OnApplicationTaskSelected( - (selected_task && selected_task->task.delayed_run_time.is_null()) - ? selected_task->task.queue_time + (selected_task && selected_task->task->delayed_run_time.is_null()) + ? selected_task->task->queue_time : TimeTicks(), lazy_now_task_selected); if (!selected_task) { @@ -436,17 +474,23 @@ WorkDetails ThreadControllerWithMessagePumpImpl::DoWorkImpl( // See https://crbug.com/681863 and https://crbug.com/874982 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "RunTask"); - // Note: all arguments after task are just passed to a TRACE_EVENT for - // logging so lambda captures are safe as lambda is executed inline. - task_annotator_.RunTask("ThreadControllerImpl::RunTask", - selected_task->task, - [&selected_task](perfetto::EventContext& ctx) { - if (selected_task->task_execution_trace_logger) - selected_task->task_execution_trace_logger.Run( - ctx, selected_task->task); - SequenceManagerImpl::MaybeEmitTaskDetails( - ctx, selected_task.value()); - }); + { + // Always track the start of the task, as this is low-overhead. + TaskAnnotator::LongTaskTracker long_task_tracker( + time_source_, *selected_task->task, &task_annotator_); + + // Note: all arguments after task are just passed to a TRACE_EVENT for + // logging so lambda captures are safe as lambda is executed inline. + task_annotator_.RunTask( + "ThreadControllerImpl::RunTask", *selected_task->task, + [&selected_task](perfetto::EventContext& ctx) { + if (selected_task->task_execution_trace_logger) + selected_task->task_execution_trace_logger.Run( + ctx, *selected_task->task); + SequenceManagerImpl::MaybeEmitTaskDetails(ctx, + selected_task.value()); + }); + } LazyNow lazy_now_after_run_task(time_source_); main_thread_only().task_source->DidRunTask(lazy_now_after_run_task); @@ -496,12 +540,12 @@ bool ThreadControllerWithMessagePumpImpl::DoIdleWork() { // Very last step before going idle, must be fast as this is hidden from the // DoIdleWork trace event below. - ~OnIdle() { run_level_tracker.OnIdle(lazy_now); } + ~OnIdle() { run_level_tracker->OnIdle(lazy_now); } LazyNow lazy_now; private: - RunLevelTracker& run_level_tracker; + const raw_ref<RunLevelTracker> run_level_tracker; }; absl::optional<OnIdle> on_idle; diff --git a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h index 66208c85826..6d322e6b2ed 100644 --- a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h +++ b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h @@ -114,7 +114,8 @@ class BASE_EXPORT ThreadControllerWithMessagePumpImpl raw_ptr<SequencedTaskSource> task_source = nullptr; // Not owned. raw_ptr<RunLoop::NestingObserver> nesting_observer = nullptr; // Not owned. - std::unique_ptr<ThreadTaskRunnerHandle> thread_task_runner_handle; + std::unique_ptr<SingleThreadTaskRunner::CurrentDefaultHandle> + thread_task_runner_handle; // Indicates that we should yield DoWork between each task to let a possibly // nested RunLoop exit. diff --git a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc index 71ac09b5360..6bbfe4963f8 100644 --- a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc +++ b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc @@ -21,7 +21,6 @@ #include "base/test/mock_callback.h" #include "base/test/scoped_feature_list.h" #include "base/test/simple_test_tick_clock.h" -#include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" @@ -231,11 +230,13 @@ class ThreadControllerWithMessagePumpTest : public testing::Test { TEST_F(ThreadControllerWithMessagePumpTest, ScheduleDelayedWork) { MockCallback<OnceClosure> task1; - task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(10))); + task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(10)), + clock_.NowTicks()); MockCallback<OnceClosure> task2; task_source_.AddTask(FROM_HERE, task2.Get()); MockCallback<OnceClosure> task3; - task_source_.AddTask(FROM_HERE, task3.Get(), FromNow(Seconds(20))); + task_source_.AddTask(FROM_HERE, task3.Get(), FromNow(Seconds(20)), + clock_.NowTicks()); // Call a no-op DoWork. Expect that it doesn't do any work. clock_.Advance(Seconds(5)); @@ -298,7 +299,8 @@ TEST_F(ThreadControllerWithMessagePumpTest, SetNextDelayedDoWork_CapAtOneDay) { TEST_F(ThreadControllerWithMessagePumpTest, DelayedWork_CapAtOneDay) { MockCallback<OnceClosure> task1; - task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Days(10))); + task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Days(10)), + clock_.NowTicks()); auto next_work_info = thread_controller_.DoWork(); EXPECT_EQ(next_work_info.delayed_run_time, FromNow(Days(1))); @@ -306,7 +308,8 @@ TEST_F(ThreadControllerWithMessagePumpTest, DelayedWork_CapAtOneDay) { TEST_F(ThreadControllerWithMessagePumpTest, DoWorkDoesntScheduleDelayedWork) { MockCallback<OnceClosure> task1; - task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(10))); + task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(10)), + clock_.NowTicks()); EXPECT_CALL(*message_pump_, ScheduleDelayedWork_TimeTicks(_)).Times(0); auto next_work_info = thread_controller_.DoWork(); @@ -446,13 +449,13 @@ TEST_F(ThreadControllerWithMessagePumpTest, SetDefaultTaskRunner) { scoped_refptr<SingleThreadTaskRunner> task_runner1 = MakeRefCounted<FakeTaskRunner>(); thread_controller_.SetDefaultTaskRunner(task_runner1); - EXPECT_EQ(task_runner1, ThreadTaskRunnerHandle::Get()); + EXPECT_EQ(task_runner1, SingleThreadTaskRunner::GetCurrentDefault()); // Check that we are correctly supporting overriding. scoped_refptr<SingleThreadTaskRunner> task_runner2 = MakeRefCounted<FakeTaskRunner>(); thread_controller_.SetDefaultTaskRunner(task_runner2); - EXPECT_EQ(task_runner2, ThreadTaskRunnerHandle::Get()); + EXPECT_EQ(task_runner2, SingleThreadTaskRunner::GetCurrentDefault()); } TEST_F(ThreadControllerWithMessagePumpTest, EnsureWorkScheduled) { @@ -735,11 +738,14 @@ TEST_F(ThreadControllerWithMessagePumpTest, NativeNestedMessageLoop) { TEST_F(ThreadControllerWithMessagePumpTest, RunWithTimeout) { MockCallback<OnceClosure> task1; - task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(5))); + task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(5)), + clock_.NowTicks()); MockCallback<OnceClosure> task2; - task_source_.AddTask(FROM_HERE, task2.Get(), FromNow(Seconds(10))); + task_source_.AddTask(FROM_HERE, task2.Get(), FromNow(Seconds(10)), + clock_.NowTicks()); MockCallback<OnceClosure> task3; - task_source_.AddTask(FROM_HERE, task3.Get(), FromNow(Seconds(20))); + task_source_.AddTask(FROM_HERE, task3.Get(), FromNow(Seconds(20)), + clock_.NowTicks()); EXPECT_CALL(*message_pump_, Run(_)) .WillOnce(Invoke([&](MessagePump::Delegate*) { @@ -767,7 +773,8 @@ TEST_F(ThreadControllerWithMessagePumpTest, RunWithTimeout) { #if BUILDFLAG(IS_WIN) TEST_F(ThreadControllerWithMessagePumpTest, SetHighResolutionTimer) { MockCallback<OnceClosure> task; - task_source_.AddTask(FROM_HERE, task.Get(), FromNow(Seconds(5))); + task_source_.AddTask(FROM_HERE, task.Get(), FromNow(Seconds(5)), + clock_.NowTicks()); ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>()); @@ -802,7 +809,8 @@ TEST_F(ThreadControllerWithMessagePumpTest, SetHighResolutionTimer) { TEST_F(ThreadControllerWithMessagePumpTest, SetHighResolutionTimerWithPowerSuspend) { MockCallback<OnceClosure> task; - task_source_.AddTask(FROM_HERE, task.Get(), FromNow(Seconds(5))); + task_source_.AddTask(FROM_HERE, task.Get(), FromNow(Seconds(5)), + clock_.NowTicks()); ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>()); @@ -844,9 +852,11 @@ TEST_F(ThreadControllerWithMessagePumpTest, ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>()); MockCallback<OnceClosure> task1; - task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(10))); + task_source_.AddTask(FROM_HERE, task1.Get(), FromNow(Seconds(10)), + clock_.NowTicks()); MockCallback<OnceClosure> task2; - task_source_.AddTask(FROM_HERE, task2.Get(), FromNow(Seconds(15))); + task_source_.AddTask(FROM_HERE, task2.Get(), FromNow(Seconds(15)), + clock_.NowTicks()); clock_.Advance(Seconds(5)); diff --git a/chromium/base/task/sequence_manager/wake_up_queue.cc b/chromium/base/task/sequence_manager/wake_up_queue.cc index 5166dbf249d..2fce8da76a9 100644 --- a/chromium/base/task/sequence_manager/wake_up_queue.cc +++ b/chromium/base/task/sequence_manager/wake_up_queue.cc @@ -27,7 +27,7 @@ void WakeUpQueue::RemoveAllCanceledDelayedTasksFromFront(LazyNow* lazy_now) { // needed because a different queue can become the top one once you remove the // canceled tasks. while (!wake_up_queue_.empty()) { - auto* top_queue = wake_up_queue_.top().queue; + auto* top_queue = wake_up_queue_.top().queue.get(); // If no tasks are removed from the top queue, then it means the top queue // cannot change anymore. diff --git a/chromium/base/task/sequence_manager/wake_up_queue.h b/chromium/base/task/sequence_manager/wake_up_queue.h index 6428aed72ea..4c15b44bb1b 100644 --- a/chromium/base/task/sequence_manager/wake_up_queue.h +++ b/chromium/base/task/sequence_manager/wake_up_queue.h @@ -87,7 +87,7 @@ class BASE_EXPORT WakeUpQueue { struct ScheduledWakeUp { WakeUp wake_up; - internal::TaskQueueImpl* queue; + raw_ptr<internal::TaskQueueImpl> queue; bool operator>(const ScheduledWakeUp& other) const { return wake_up.latest_time() > other.wake_up.latest_time(); diff --git a/chromium/base/task/sequence_manager/wake_up_queue_unittest.cc b/chromium/base/task/sequence_manager/wake_up_queue_unittest.cc index e4850696ca9..b6e96eb13ed 100644 --- a/chromium/base/task/sequence_manager/wake_up_queue_unittest.cc +++ b/chromium/base/task/sequence_manager/wake_up_queue_unittest.cc @@ -77,6 +77,8 @@ class MockWakeUpQueue : public WakeUpQueue { class WakeUpQueueTest : public testing::Test { public: void SetUp() final { + // A null clock triggers some assertions. + tick_clock_.Advance(Milliseconds(1)); wake_up_queue_ = std::make_unique<MockWakeUpQueue>(); task_queue_ = std::make_unique<TaskQueueImplForTest>( nullptr, wake_up_queue_.get(), TaskQueue::Spec(QueueName::TEST_TQ)); diff --git a/chromium/base/task/sequence_manager/work_deduplicator.cc b/chromium/base/task/sequence_manager/work_deduplicator.cc index 05a1bbb1a78..407d36db601 100644 --- a/chromium/base/task/sequence_manager/work_deduplicator.cc +++ b/chromium/base/task/sequence_manager/work_deduplicator.cc @@ -4,6 +4,7 @@ #include "base/task/sequence_manager/work_deduplicator.h" +#include <ostream> #include <utility> #include "base/check_op.h" diff --git a/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc b/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc index 49dc854f95d..10310863594 100644 --- a/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc +++ b/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc @@ -78,7 +78,7 @@ class WorkQueueSetsTest : public testing::Test { task_order.delayed_run_time(), subtle::DelayPolicy::kFlexibleNoSooner), EnqueueOrder::FromIntForTesting(task_order.sequence_num()), - task_order.enqueue_order()); + task_order.enqueue_order(), TimeTicks() + Milliseconds(1)); return fake_task; } diff --git a/chromium/base/task/sequence_manager/work_queue_unittest.cc b/chromium/base/task/sequence_manager/work_queue_unittest.cc index 60c0feac3c6..2c5291f1a72 100644 --- a/chromium/base/task/sequence_manager/work_queue_unittest.cc +++ b/chromium/base/task/sequence_manager/work_queue_unittest.cc @@ -97,7 +97,7 @@ class WorkQueueTest : public testing::Test { task_order.delayed_run_time(), subtle::DelayPolicy::kFlexibleNoSooner), EnqueueOrder::FromIntForTesting(task_order.sequence_num()), - task_order.enqueue_order()); + task_order.enqueue_order(), TimeTicks() + Milliseconds(1)); return fake_task; } diff --git a/chromium/base/task/sequenced_task_runner.h b/chromium/base/task/sequenced_task_runner.h index 416f3bc9ff1..33213f979d8 100644 --- a/chromium/base/task/sequenced_task_runner.h +++ b/chromium/base/task/sequenced_task_runner.h @@ -17,8 +17,8 @@ namespace blink { class LowPrecisionTimer; -class MetronomeSource; class TimerBase; +class TimerBasedTickProvider; class WebRtcTaskQueue; } namespace webrtc { @@ -55,8 +55,8 @@ class PostDelayedTaskPassKey { friend class base::DeadlineTimer; friend class base::MetronomeTimer; friend class blink::LowPrecisionTimer; - friend class blink::MetronomeSource; friend class blink::TimerBase; + friend class blink::TimerBasedTickProvider; friend class blink::WebRtcTaskQueue; friend class PostDelayedTaskPassKeyForTesting; friend class webrtc::ThreadWrapper; diff --git a/chromium/base/task/sequenced_task_runner_unittest.cc b/chromium/base/task/sequenced_task_runner_unittest.cc index 704552513d7..c4fd0d8c4c9 100644 --- a/chromium/base/task/sequenced_task_runner_unittest.cc +++ b/chromium/base/task/sequenced_task_runner_unittest.cc @@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/callback_helpers.h" #include "base/gtest_prod_util.h" #include "base/location.h" #include "base/memory/raw_ptr.h" @@ -81,8 +82,7 @@ TEST_F(SequencedTaskRunnerTest, OnTaskRunnerDeleterOnMainThread) { SequencedTaskRunner::GetCurrentDefault()), OnTaskRunnerDeleter(SequencedTaskRunner::GetCurrentDefault())); EXPECT_FALSE(deleted_on_main_thread); - foreign_runner_->PostTask( - FROM_HERE, BindOnce([](SequenceBoundUniquePtr) {}, std::move(ptr))); + foreign_runner_->PostTask(FROM_HERE, DoNothingWithBoundArgs(std::move(ptr))); { RunLoop run_loop; diff --git a/chromium/base/task/single_thread_task_executor.cc b/chromium/base/task/single_thread_task_executor.cc index 73e49422224..be60ec98845 100644 --- a/chromium/base/task/single_thread_task_executor.cc +++ b/chromium/base/task/single_thread_task_executor.cc @@ -33,7 +33,7 @@ SingleThreadTaskExecutor::SingleThreadTaskExecutor( sequence_manager::QueueName::DEFAULT_TQ))), type_(type), simple_task_executor_(task_runner()) { - sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner()); + sequence_manager_->SetDefaultTaskRunner(task_runner()); sequence_manager_->BindToMessagePump(std::move(pump)); } diff --git a/chromium/base/task/single_thread_task_executor_unittest.cc b/chromium/base/task/single_thread_task_executor_unittest.cc index 57c008e94a0..ba694e8dff9 100644 --- a/chromium/base/task/single_thread_task_executor_unittest.cc +++ b/chromium/base/task/single_thread_task_executor_unittest.cc @@ -35,7 +35,6 @@ #include "base/threading/platform_thread.h" #include "base/threading/sequence_local_storage_slot.h" #include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" @@ -287,7 +286,7 @@ class DummyTaskObserver : public TaskObserver { void RecursiveFunc(TaskList* order, int cookie, int depth) { order->RecordStart(RECURSIVE, cookie); if (depth > 0) { - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&RecursiveFunc, order, cookie, depth - 1)); } order->RecordEnd(RECURSIVE, cookie); @@ -548,20 +547,20 @@ TEST_P(SingleThreadTaskExecutorTypedTest, PostTask) { // Add tests to message loop scoped_refptr<Foo> foo(new Foo()); std::string a("a"), b("b"), c("c"), d("d"); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&Foo::Test0, foo)); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&Foo::Test0, foo)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&Foo::Test1Ptr, foo, &b)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&Foo::Test1Int, foo, 100)); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&Foo::Test1Ptr, foo, &b)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&Foo::Test1Int, foo, 100)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&Foo::Test2Ptr, foo, &a, &c)); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&Foo::Test2Mixed, foo, a, &d)); // After all tests, post a message that will shut down the message loop - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&RunLoop::QuitCurrentWhenIdleDeprecated)); // Now kick things off @@ -747,7 +746,7 @@ class RecordDeletionProbe : public RefCounted<RecordDeletionProbe> { ~RecordDeletionProbe() { *was_deleted_ = true; if (post_on_delete_.get()) - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&RecordDeletionProbe::Run, post_on_delete_)); } @@ -808,8 +807,8 @@ namespace { void NestingFunc(int* depth) { if (*depth > 0) { *depth -= 1; - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&NestingFunc, depth)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&NestingFunc, depth)); RunLoop(RunLoop::Type::kNestableTasksAllowed).Run(); } @@ -822,8 +821,8 @@ TEST_P(SingleThreadTaskExecutorTypedTest, Nesting) { SingleThreadTaskExecutor executor(GetParam()); int depth = 50; - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&NestingFunc, &depth)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&NestingFunc, &depth)); RunLoop().Run(); EXPECT_EQ(depth, 0); } @@ -832,12 +831,12 @@ TEST_P(SingleThreadTaskExecutorTypedTest, Recursive) { SingleThreadTaskExecutor executor(GetParam()); TaskList order; - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&RecursiveFunc, &order, 1, 2)); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&RecursiveFunc, &order, 2, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&QuitFunc, &order, 3)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&QuitFunc, &order, 3)); RunLoop().Run(); @@ -874,12 +873,12 @@ TEST_P(SingleThreadTaskExecutorTypedTest, NonNestableWithNoNesting) { TaskList order; - ThreadTaskRunnerHandle::Get()->PostNonNestableTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostNonNestableTask( FROM_HERE, BindOnce(&OrderedFunc, &order, 1)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&QuitFunc, &order, 3)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&QuitFunc, &order, 3)); RunLoop().Run(); // FIFO order. @@ -914,17 +913,17 @@ TEST_P(SingleThreadTaskExecutorTypedTest, NonNestableDelayedInNestedLoop) { TaskList order; - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&FuncThatPumps, &order, 1)); - ThreadTaskRunnerHandle::Get()->PostNonNestableTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&FuncThatPumps, &order, 1)); + SingleThreadTaskRunner::GetCurrentDefault()->PostNonNestableTask( FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 3)); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 3)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&SleepFunc, &order, 4, Milliseconds(50))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 5)); - ThreadTaskRunnerHandle::Get()->PostNonNestableTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 5)); + SingleThreadTaskRunner::GetCurrentDefault()->PostNonNestableTask( FROM_HERE, BindOnce(&QuitFunc, &order, 6)); RunLoop().Run(); @@ -967,18 +966,18 @@ TEST_P(SingleThreadTaskExecutorTypedTest, QuitNow) { RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&FuncThatQuitsNow)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 3)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&FuncThatQuitsNow)); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&FuncThatQuitsNow)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 3)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&FuncThatQuitsNow)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&OrderedFunc, &order, 4)); // never runs RunLoop().Run(); @@ -1003,15 +1002,15 @@ TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitTop) { RunLoop outer_run_loop; RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - outer_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_run_loop.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, outer_run_loop.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, nested_run_loop.QuitClosure()); outer_run_loop.Run(); @@ -1033,15 +1032,15 @@ TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitNested) { RunLoop outer_run_loop; RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - outer_run_loop.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, nested_run_loop.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, outer_run_loop.QuitClosure()); outer_run_loop.Run(); @@ -1074,9 +1073,9 @@ TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopNestedAfterQuit) { RunLoop outer_run_loop; RunLoop nested_run_loop; - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, nested_run_loop.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&QuitAndRunNestedLoop, &order, 1, &outer_run_loop, &nested_run_loop)); @@ -1099,17 +1098,17 @@ TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitBogus) { RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); RunLoop bogus_run_loop; - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - bogus_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - outer_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_run_loop.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, bogus_run_loop.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, outer_run_loop.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, nested_run_loop.QuitClosure()); outer_run_loop.Run(); @@ -1134,36 +1133,36 @@ TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitDeep) { RunLoop nested_loop3(RunLoop::Type::kNestableTasksAllowed); RunLoop nested_loop4(RunLoop::Type::kNestableTasksAllowed); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_loop1))); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatRuns, &order, 2, Unretained(&nested_loop2))); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatRuns, &order, 3, Unretained(&nested_loop3))); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatRuns, &order, 4, Unretained(&nested_loop4))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 5)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - outer_run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 6)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_loop1.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 7)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_loop2.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 8)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_loop3.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 9)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - nested_loop4.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 10)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 5)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, outer_run_loop.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 6)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, nested_loop1.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 7)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, nested_loop2.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 8)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, nested_loop3.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 9)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, nested_loop4.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 10)); outer_run_loop.Run(); @@ -1200,9 +1199,9 @@ TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderBefore) { run_loop.Quit(); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&OrderedFunc, &order, 1)); // never runs - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatQuitsNow)); // never runs run_loop.Run(); @@ -1218,12 +1217,13 @@ TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderDuring) { RunLoop run_loop; - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 1)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure()); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 1)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, + run_loop.QuitClosure()); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); // never runs - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatQuitsNow)); // never runs run_loop.Run(); @@ -1243,21 +1243,21 @@ TEST_P(SingleThreadTaskExecutorTypedTest, RunLoopQuitOrderAfter) { RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop))); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 2)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&FuncThatQuitsNow)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 3)); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 2)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&FuncThatQuitsNow)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 3)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, nested_run_loop.QuitClosure()); // has no affect - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&OrderedFunc, &order, 4)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&FuncThatQuitsNow)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&OrderedFunc, &order, 4)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&FuncThatQuitsNow)); nested_run_loop.allow_quit_current_deprecated_ = true; @@ -1302,9 +1302,11 @@ TEST_P(SingleThreadTaskExecutorTypedTest, MAYBE_RecursivePostsDoNotFloodPipe) { run_loop.Run(); } -TEST_P(SingleThreadTaskExecutorTypedTest, NestableTasksAllowedAtTopLevel) { +TEST_P(SingleThreadTaskExecutorTypedTest, + ApplicationTasksAllowedInNativeNestedLoopAtTopLevel) { SingleThreadTaskExecutor executor(GetParam()); - EXPECT_TRUE(CurrentThread::Get()->NestableTasksAllowed()); + EXPECT_TRUE( + CurrentThread::Get()->ApplicationTasksAllowedInNativeNestedLoop()); } // Nestable tasks shouldn't be allowed to run reentrantly by default (regression @@ -1316,7 +1318,8 @@ TEST_P(SingleThreadTaskExecutorTypedTest, NestableTasksDisallowedByDefault) { FROM_HERE, BindOnce( [](RunLoop* run_loop) { - EXPECT_FALSE(CurrentThread::Get()->NestableTasksAllowed()); + EXPECT_FALSE(CurrentThread::Get() + ->ApplicationTasksAllowedInNativeNestedLoop()); run_loop->Quit(); }, Unretained(&run_loop))); @@ -1335,7 +1338,7 @@ TEST_P(SingleThreadTaskExecutorTypedTest, // kNestableTasksAllowed (i.e. this is testing that this is // processed and doesn't hang). RunLoop nested_run_loop(RunLoop::Type::kNestableTasksAllowed); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce( [](RunLoop* nested_run_loop) { @@ -1345,7 +1348,8 @@ TEST_P(SingleThreadTaskExecutorTypedTest, // nestable tasks are by default disallowed from this // layer. EXPECT_FALSE( - CurrentThread::Get()->NestableTasksAllowed()); + CurrentThread::Get() + ->ApplicationTasksAllowedInNativeNestedLoop()); nested_run_loop->Quit(); }, Unretained(&nested_run_loop))); @@ -1358,7 +1362,7 @@ TEST_P(SingleThreadTaskExecutorTypedTest, } TEST_P(SingleThreadTaskExecutorTypedTest, - NestableTasksAllowedExplicitlyInScope) { + ApplicationTasksAllowedInNativeNestedLoopExplicitlyInScope) { SingleThreadTaskExecutor executor(GetParam()); RunLoop run_loop; executor.task_runner()->PostTask( @@ -1368,9 +1372,11 @@ TEST_P(SingleThreadTaskExecutorTypedTest, { CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow_nestable_tasks; - EXPECT_TRUE(CurrentThread::Get()->NestableTasksAllowed()); + EXPECT_TRUE(CurrentThread::Get() + ->ApplicationTasksAllowedInNativeNestedLoop()); } - EXPECT_FALSE(CurrentThread::Get()->NestableTasksAllowed()); + EXPECT_FALSE(CurrentThread::Get() + ->ApplicationTasksAllowedInNativeNestedLoop()); run_loop->Quit(); }, Unretained(&run_loop))); @@ -1510,8 +1516,8 @@ bool QuitOnSystemTimer(UINT message, LPARAM lparam, LRESULT* result) { if (message == static_cast<UINT>(WM_TIMER)) { - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - BindOnce(&::PostQuitMessage, 0)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, BindOnce(&::PostQuitMessage, 0)); } *result = 0; return true; @@ -1524,7 +1530,7 @@ bool DelayedQuitOnSystemTimer(UINT message, LPARAM lparam, LRESULT* result) { if (message == static_cast<UINT>(WM_TIMER)) { - ThreadTaskRunnerHandle::Get()->PostDelayedTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( FROM_HERE, BindOnce(&::PostQuitMessage, 0), Milliseconds(10)); } *result = 0; @@ -1760,8 +1766,9 @@ void RunTest_NestingDenial2(MessagePumpType message_pump_type) { TaskList order; win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); worker.task_runner()->PostTask( - FROM_HERE, BindOnce(&RecursiveFuncWin, ThreadTaskRunnerHandle::Get(), - event.get(), true, &order, false)); + FROM_HERE, + BindOnce(&RecursiveFuncWin, SingleThreadTaskRunner::GetCurrentDefault(), + event.get(), true, &order, false)); // Let the other thread execute. WaitForSingleObject(event.get(), INFINITE); RunLoop().Run(); @@ -1810,8 +1817,9 @@ TEST(SingleThreadTaskExecutorTest, NestingSupport2) { TaskList order; win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); worker.task_runner()->PostTask( - FROM_HERE, BindOnce(&RecursiveFuncWin, ThreadTaskRunnerHandle::Get(), - event.get(), false, &order, true)); + FROM_HERE, + BindOnce(&RecursiveFuncWin, SingleThreadTaskRunner::GetCurrentDefault(), + event.get(), false, &order, true)); // Let the other thread execute. WaitForSingleObject(event.get(), INFINITE); RunLoop().Run(); @@ -1972,11 +1980,11 @@ TEST(SingleThreadTaskExecutorTest, ThreadMainTaskRunner) { scoped_refptr<Foo> foo(new Foo()); std::string a("a"); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&Foo::Test1ConstRef, foo, a)); // Post quit task; - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindOnce(&RunLoop::QuitCurrentWhenIdleDeprecated)); // Now kick things off @@ -1995,10 +2003,10 @@ TEST(SingleThreadTaskExecutorTest, type) { void EmptyFunction() {} void PostMultipleTasks() { - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::BindOnce(&EmptyFunction)); - ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::BindOnce(&EmptyFunction)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(&EmptyFunction)); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(&EmptyFunction)); } static const int kSignalMsg = WM_USER + 2; @@ -2028,11 +2036,11 @@ LRESULT CALLBACK TestWndProcThunk(HWND hwnd, // First, we post a task that will post multiple no-op tasks to make sure // that the pump's incoming task queue does not become empty during the // test. - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&PostMultipleTasks)); // Next, we post a task that posts a windows message to trigger the second // stage of the test. - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&PostWindowsMessage, hwnd)); break; case 2: @@ -2041,7 +2049,7 @@ LRESULT CALLBACK TestWndProcThunk(HWND hwnd, CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow_nestable_tasks; bool did_run = false; - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&EndTest, &did_run, hwnd)); // Run a nested windows-style message loop and verify that our task runs. // If it doesn't, then we'll loop here until the test times out. @@ -2093,10 +2101,10 @@ TEST(SingleThreadTaskExecutorTest, SequenceLocalStorageSetGet) { SequenceLocalStorageSlot<int> slot; - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindLambdaForTesting([&]() { slot.emplace(11); })); - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindLambdaForTesting([&]() { EXPECT_EQ(*slot, 11); })); RunLoop().RunUntilIdle(); @@ -2110,7 +2118,7 @@ TEST(SingleThreadTaskExecutorTest, SequenceLocalStorageDifferentMessageLoops) { { SingleThreadTaskExecutor executor; - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindLambdaForTesting([&]() { slot.emplace(11); })); RunLoop().RunUntilIdle(); @@ -2118,7 +2126,7 @@ TEST(SingleThreadTaskExecutorTest, SequenceLocalStorageDifferentMessageLoops) { } SingleThreadTaskExecutor executor; - ThreadTaskRunnerHandle::Get()->PostTask( + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, BindLambdaForTesting([&]() { EXPECT_FALSE(slot); })); RunLoop().RunUntilIdle(); @@ -2139,9 +2147,9 @@ class PostTaskOnDestroy { // Post a task that will repost itself on destruction |times| times. static void PostTaskWithPostingDestructor(int times) { if (times > 0) { - ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, BindOnce([](std::unique_ptr<PostTaskOnDestroy>) {}, - std::make_unique<PostTaskOnDestroy>(times - 1))); + SingleThreadTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, DoNothingWithBoundArgs( + std::make_unique<PostTaskOnDestroy>(times - 1))); } } diff --git a/chromium/base/task/task_features.cc b/chromium/base/task/task_features.cc index d12481f40d2..5e03b8a7aca 100644 --- a/chromium/base/task/task_features.cc +++ b/chromium/base/task/task_features.cc @@ -4,19 +4,27 @@ #include "base/task/task_features.h" +#include <atomic> + #include "base/base_export.h" #include "base/feature_list.h" +#include "base/threading/platform_thread.h" namespace base { -#if HAS_NATIVE_THREAD_POOL() -BASE_FEATURE(kUseNativeThreadPool, - "UseNativeThreadPool", - base::FEATURE_DISABLED_BY_DEFAULT); -BASE_FEATURE(kUseBackgroundNativeThreadPool, - "UseBackgroundNativeThreadPool", +// Note to implementers: thread pool code using task features must absolutely +// not invoke FeatureList::IsEnabled outside of the main thread. Doing so +// causes data races between worker threads and ~FeatureList when tests end +// (crbug.com/1344573). A reliable moment to query and cache the feature state +// is on ThreadPoolImpl::Start (and thus also on the first WorkerThread::Start, +// not the later ones) as this is invoked from the main thread after +// initializing the FeatureList. If caching the feature state in a static, you +// must be aware that all tests sharing a process will have the same state, +// regardless of future ScopedFeatureList instances. + +BASE_FEATURE(kUseUtilityThreadGroup, + "UseUtilityThreadGroup", base::FEATURE_DISABLED_BY_DEFAULT); -#endif BASE_FEATURE(kNoWorkerThreadReclaim, "NoWorkerThreadReclaim", @@ -25,14 +33,18 @@ BASE_FEATURE(kNoWorkerThreadReclaim, // static BASE_FEATURE(kNoWakeUpsForCanceledTasks, "NoWakeUpsForCanceledTasks", - FEATURE_DISABLED_BY_DEFAULT); + FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kRemoveCanceledTasksInTaskQueue, "RemoveCanceledTasksInTaskQueue2", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kAlwaysAbandonScheduledTask, "AlwaysAbandonScheduledTask", + base::FEATURE_ENABLED_BY_DEFAULT); + +BASE_FEATURE(kDelayFirstWorkerWake, + "DelayFirstWorkerWake", base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kAddTaskLeewayFeature, @@ -84,7 +96,16 @@ BASE_EXPORT void InitializeTaskLeeway() { g_task_leeway.store(kTaskLeewayParam.Get(), std::memory_order_relaxed); } -BASE_EXPORT TimeDelta GetTaskLeeway() { +BASE_EXPORT TimeDelta GetTaskLeewayForCurrentThread() { + // For some threads, there might be a override of the leeway, so check it + // first. + auto leeway_override = PlatformThread::GetThreadLeewayOverride(); + if (leeway_override.has_value()) + return leeway_override.value(); + return g_task_leeway.load(std::memory_order_relaxed); +} + +BASE_EXPORT TimeDelta GetDefaultTaskLeeway() { return g_task_leeway.load(std::memory_order_relaxed); } diff --git a/chromium/base/task/task_features.h b/chromium/base/task/task_features.h index f41e62817ff..c9092580c47 100644 --- a/chromium/base/task/task_features.h +++ b/chromium/base/task/task_features.h @@ -12,21 +12,9 @@ namespace base { -#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) -#define HAS_NATIVE_THREAD_POOL() 1 -#else -#define HAS_NATIVE_THREAD_POOL() 0 -#endif - -#if HAS_NATIVE_THREAD_POOL() -// Under this feature, ThreadPoolImpl will use a foreground ThreadGroup backed -// by a native thread pool implementation. The Windows Thread Pool API and -// libdispatch are used on Windows and macOS/iOS respectively. -BASE_EXPORT BASE_DECLARE_FEATURE(kUseNativeThreadPool); -// Under this feature, ThreadPoolImpl will use a background ThreadGroup backed -// by a native thread pool implementation. -BASE_EXPORT BASE_DECLARE_FEATURE(kUseBackgroundNativeThreadPool); -#endif +// Under this feature, a utility_thread_group will be created for +// running USER_VISIBLE tasks. +BASE_EXPORT BASE_DECLARE_FEATURE(kUseUtilityThreadGroup); // Under this feature, worker threads are not reclaimed after a timeout. Rather, // only excess workers are cleaned up immediately after finishing a task. @@ -45,6 +33,11 @@ BASE_EXPORT BASE_DECLARE_FEATURE(kRemoveCanceledTasksInTaskQueue); // kRemoveCanceledTasksInTaskQueue. BASE_EXPORT BASE_DECLARE_FEATURE(kAlwaysAbandonScheduledTask); +// This feature controls whether ThreadPool WorkerThreads should hold off waking +// up to purge partition alloc within the first minute of their lifetime. See +// base::internal::GetSleepTimeBeforePurge. +BASE_EXPORT BASE_DECLARE_FEATURE(kDelayFirstWorkerWake); + // Under this feature, a non-zero leeway is added to delayed tasks. Along with // DelayPolicy, this affects the time at which a delayed task runs. BASE_EXPORT BASE_DECLARE_FEATURE(kAddTaskLeewayFeature); @@ -74,7 +67,8 @@ extern const BASE_EXPORT base::FeatureParam<TimeDelta> kBrowserPeriodicYieldingToNativeDelay; BASE_EXPORT void InitializeTaskLeeway(); -BASE_EXPORT TimeDelta GetTaskLeeway(); +BASE_EXPORT TimeDelta GetTaskLeewayForCurrentThread(); +BASE_EXPORT TimeDelta GetDefaultTaskLeeway(); } // namespace base diff --git a/chromium/base/task/task_runner.cc b/chromium/base/task/task_runner.cc index 61226b57f34..2b77db63e84 100644 --- a/chromium/base/task/task_runner.cc +++ b/chromium/base/task/task_runner.cc @@ -28,7 +28,7 @@ class PostTaskAndReplyTaskRunner : public internal::PostTaskAndReplyImpl { bool PostTask(const Location& from_here, OnceClosure task) override; // Non-owning. - raw_ptr<TaskRunner> destination_; + raw_ptr<TaskRunner, DanglingUntriaged> destination_; }; PostTaskAndReplyTaskRunner::PostTaskAndReplyTaskRunner( diff --git a/chromium/base/task/task_runner_unittest.cc b/chromium/base/task/task_runner_unittest.cc index 668d8addcd7..d8440466fd1 100644 --- a/chromium/base/task/task_runner_unittest.cc +++ b/chromium/base/task/task_runner_unittest.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/task/task_runner_util.h" +#include "base/task/task_runner.h" #include <memory> #include <utility> @@ -10,8 +10,8 @@ #include "base/bind.h" #include "base/location.h" #include "base/run_loop.h" +#include "base/task/single_thread_task_runner.h" #include "base/test/task_environment.h" -#include "base/threading/thread_task_runner_handle.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -96,7 +96,7 @@ TEST_F(TaskRunnerTest, PostTaskAndReplyWithResult) { int result = 0; test::SingleThreadTaskEnvironment task_environment; - ThreadTaskRunnerHandle::Get()->PostTaskAndReplyWithResult( + SingleThreadTaskRunner::GetCurrentDefault()->PostTaskAndReplyWithResult( FROM_HERE, BindOnce(&ReturnFourtyTwo), BindOnce(&StoreValue, &result)); RunLoop().RunUntilIdle(); @@ -108,7 +108,7 @@ TEST_F(TaskRunnerTest, PostTaskAndReplyWithResultRepeatingCallbacks) { int result = 0; test::SingleThreadTaskEnvironment task_environment; - ThreadTaskRunnerHandle::Get()->PostTaskAndReplyWithResult( + SingleThreadTaskRunner::GetCurrentDefault()->PostTaskAndReplyWithResult( FROM_HERE, BindRepeating(&ReturnFourtyTwo), BindRepeating(&StoreValue, &result)); @@ -121,7 +121,7 @@ TEST_F(TaskRunnerTest, PostTaskAndReplyWithResultImplicitConvert) { double result = 0; test::SingleThreadTaskEnvironment task_environment; - ThreadTaskRunnerHandle::Get()->PostTaskAndReplyWithResult( + SingleThreadTaskRunner::GetCurrentDefault()->PostTaskAndReplyWithResult( FROM_HERE, BindOnce(&ReturnFourtyTwo), BindOnce(&StoreDoubleValue, &result)); @@ -132,7 +132,7 @@ TEST_F(TaskRunnerTest, PostTaskAndReplyWithResultImplicitConvert) { TEST_F(TaskRunnerTest, PostTaskAndReplyWithResultPassed) { test::SingleThreadTaskEnvironment task_environment; - ThreadTaskRunnerHandle::Get()->PostTaskAndReplyWithResult( + SingleThreadTaskRunner::GetCurrentDefault()->PostTaskAndReplyWithResult( FROM_HERE, BindOnce(&CreateFoo), BindOnce(&ExpectFoo)); RunLoop().RunUntilIdle(); @@ -143,7 +143,7 @@ TEST_F(TaskRunnerTest, PostTaskAndReplyWithResultPassed) { TEST_F(TaskRunnerTest, PostTaskAndReplyWithResultPassedFreeProc) { test::SingleThreadTaskEnvironment task_environment; - ThreadTaskRunnerHandle::Get()->PostTaskAndReplyWithResult( + SingleThreadTaskRunner::GetCurrentDefault()->PostTaskAndReplyWithResult( FROM_HERE, BindOnce(&CreateScopedFoo), BindOnce(&ExpectScopedFoo)); RunLoop().RunUntilIdle(); @@ -158,7 +158,7 @@ TEST_F(TaskRunnerTest, PostTaskAndReplyWithResultWithoutDefaultConstructor) { test::SingleThreadTaskEnvironment task_environment; int actual = 0; - ThreadTaskRunnerHandle::Get()->PostTaskAndReplyWithResult( + SingleThreadTaskRunner::GetCurrentDefault()->PostTaskAndReplyWithResult( FROM_HERE, BindOnce(&CreateFooWithoutDefaultConstructor, kSomeVal), BindOnce(&SaveFooWithoutDefaultConstructor, &actual)); diff --git a/chromium/base/task/task_runner_util.h b/chromium/base/task/task_runner_util.h deleted file mode 100644 index c28f8ce908e..00000000000 --- a/chromium/base/task/task_runner_util.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TASK_TASK_RUNNER_UTIL_H_ -#define BASE_TASK_TASK_RUNNER_UTIL_H_ - -#include <memory> -#include <utility> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/check.h" -#include "base/task/post_task_and_reply_with_result_internal.h" -#include "base/task/task_runner.h" - -namespace base { - -// When you have these methods -// -// R DoWorkAndReturn(); -// void Callback(const R& result); -// -// and want to call them in a PostTaskAndReply kind of fashion where the -// result of DoWorkAndReturn is passed to the Callback, you can use -// PostTaskAndReplyWithResult as in this example: -// -// PostTaskAndReplyWithResult( -// target_thread_.task_runner(), -// FROM_HERE, -// BindOnce(&DoWorkAndReturn), -// BindOnce(&Callback)); -// -// DEPRECATED: Prefer calling|task_runner->PostTaskAndReplyWithResult(...)| -// directly. -// TODO(gab): Mass-migrate to the member method. -template <typename TaskReturnType, typename ReplyArgType> -bool PostTaskAndReplyWithResult(TaskRunner* task_runner, - const Location& from_here, - OnceCallback<TaskReturnType()> task, - OnceCallback<void(ReplyArgType)> reply) { - DCHECK(task); - DCHECK(reply); - // std::unique_ptr used to avoid the need of a default constructor. - auto* result = new std::unique_ptr<TaskReturnType>(); - return task_runner->PostTaskAndReply( - from_here, - BindOnce(&internal::ReturnAsParamAdapter<TaskReturnType>, std::move(task), - result), - BindOnce(&internal::ReplyAdapter<TaskReturnType, ReplyArgType>, - std::move(reply), Owned(result))); -} - -} // namespace base - -#endif // BASE_TASK_TASK_RUNNER_UTIL_H_ diff --git a/chromium/base/task/task_runner_util_unittest.cc b/chromium/base/task/task_runner_util_unittest.cc deleted file mode 100644 index 3a9ce45459b..00000000000 --- a/chromium/base/task/task_runner_util_unittest.cc +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/task/task_runner_util.h" - -#include <memory> -#include <utility> - -#include "base/bind.h" -#include "base/location.h" -#include "base/run_loop.h" -#include "base/test/task_environment.h" -#include "base/threading/thread_task_runner_handle.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -namespace { - -int ReturnFourtyTwo() { - return 42; -} - -void StoreValue(int* destination, int value) { - *destination = value; -} - -void StoreDoubleValue(double* destination, double value) { - *destination = value; -} - -int g_foo_destruct_count = 0; -int g_foo_free_count = 0; - -struct Foo { - ~Foo() { - ++g_foo_destruct_count; - } -}; - -std::unique_ptr<Foo> CreateFoo() { - return std::make_unique<Foo>(); -} - -void ExpectFoo(std::unique_ptr<Foo> foo) { - EXPECT_TRUE(foo.get()); - std::unique_ptr<Foo> local_foo(std::move(foo)); - EXPECT_TRUE(local_foo.get()); - EXPECT_FALSE(foo.get()); -} - -struct FooDeleter { - void operator()(Foo* foo) const { - ++g_foo_free_count; - delete foo; - } -}; - -std::unique_ptr<Foo, FooDeleter> CreateScopedFoo() { - return std::unique_ptr<Foo, FooDeleter>(new Foo); -} - -void ExpectScopedFoo(std::unique_ptr<Foo, FooDeleter> foo) { - EXPECT_TRUE(foo.get()); - std::unique_ptr<Foo, FooDeleter> local_foo(std::move(foo)); - EXPECT_TRUE(local_foo.get()); - EXPECT_FALSE(foo.get()); -} - -struct FooWithoutDefaultConstructor { - explicit FooWithoutDefaultConstructor(int value) : value(value) {} - int value; -}; - -FooWithoutDefaultConstructor CreateFooWithoutDefaultConstructor(int value) { - return FooWithoutDefaultConstructor(value); -} - -void SaveFooWithoutDefaultConstructor(int* output_value, - FooWithoutDefaultConstructor input) { - *output_value = input.value; -} - -} // namespace - -TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResult) { - int result = 0; - - test::TaskEnvironment task_environment; - PostTaskAndReplyWithResult(ThreadTaskRunnerHandle::Get().get(), FROM_HERE, - BindOnce(&ReturnFourtyTwo), - BindOnce(&StoreValue, &result)); - - RunLoop().RunUntilIdle(); - - EXPECT_EQ(42, result); -} - -TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResultImplicitConvert) { - double result = 0; - - test::TaskEnvironment task_environment; - PostTaskAndReplyWithResult(ThreadTaskRunnerHandle::Get().get(), FROM_HERE, - BindOnce(&ReturnFourtyTwo), - BindOnce(&StoreDoubleValue, &result)); - - RunLoop().RunUntilIdle(); - - EXPECT_DOUBLE_EQ(42.0, result); -} - -TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResultPassed) { - g_foo_destruct_count = 0; - g_foo_free_count = 0; - - test::TaskEnvironment task_environment; - PostTaskAndReplyWithResult(ThreadTaskRunnerHandle::Get().get(), FROM_HERE, - BindOnce(&CreateFoo), BindOnce(&ExpectFoo)); - - RunLoop().RunUntilIdle(); - - EXPECT_EQ(1, g_foo_destruct_count); - EXPECT_EQ(0, g_foo_free_count); -} - -TEST(TaskRunnerHelpersTest, PostTaskAndReplyWithResultPassedFreeProc) { - g_foo_destruct_count = 0; - g_foo_free_count = 0; - - test::TaskEnvironment task_environment; - PostTaskAndReplyWithResult(ThreadTaskRunnerHandle::Get().get(), FROM_HERE, - BindOnce(&CreateScopedFoo), - BindOnce(&ExpectScopedFoo)); - - RunLoop().RunUntilIdle(); - - EXPECT_EQ(1, g_foo_destruct_count); - EXPECT_EQ(1, g_foo_free_count); -} - -TEST(TaskRunnerHelpersTest, - PostTaskAndReplyWithResultWithoutDefaultConstructor) { - const int kSomeVal = 17; - - test::TaskEnvironment task_environment; - int actual = 0; - PostTaskAndReplyWithResult( - ThreadTaskRunnerHandle::Get().get(), FROM_HERE, - BindOnce(&CreateFooWithoutDefaultConstructor, kSomeVal), - BindOnce(&SaveFooWithoutDefaultConstructor, &actual)); - - RunLoop().RunUntilIdle(); - - EXPECT_EQ(kSomeVal, actual); -} - -} // namespace base diff --git a/chromium/base/task/thread_pool/delayed_task_manager.cc b/chromium/base/task/thread_pool/delayed_task_manager.cc index 856444491e2..fdc391dc42b 100644 --- a/chromium/base/task/thread_pool/delayed_task_manager.cc +++ b/chromium/base/task/thread_pool/delayed_task_manager.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/check.h" #include "base/feature_list.h" +#include "base/task/common/checked_lock.h" #include "base/task/sequenced_task_runner.h" #include "base/task/task_features.h" #include "base/task/task_runner.h" @@ -87,6 +88,7 @@ void DelayedTaskManager::AddDelayedTask( scoped_refptr<TaskRunner> task_runner) { DCHECK(task.task); DCHECK(!task.delayed_run_time.is_null()); + DCHECK(!task.queue_time.is_null()); // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167 // for details. @@ -97,12 +99,10 @@ void DelayedTaskManager::AddDelayedTask( CheckedAutoLock auto_lock(queue_lock_); auto [old_process_ripe_tasks_time, old_delay_policy] = GetTimeAndDelayPolicyToScheduleProcessRipeTasksLockRequired(); - pending_high_res_task_count_ += - (task.delay_policy == subtle::DelayPolicy::kPrecise); delayed_task_queue_.insert(DelayedTask(std::move(task), std::move(post_task_now_callback), std::move(task_runner))); - // Not started yet. + // Not started or already shutdown. if (service_thread_task_runner_ == nullptr) return; @@ -126,6 +126,11 @@ void DelayedTaskManager::ProcessRipeTasks() { { CheckedAutoLock auto_lock(queue_lock_); + + // Already shutdown. + if (!service_thread_task_runner_) + return; + const TimeTicks now = tick_clock_->NowTicks(); // A delayed task is ripe if it reached its delayed run time or if it is // canceled. If it is canceled, schedule its deletion on the correct @@ -139,10 +144,6 @@ void DelayedTaskManager::ProcessRipeTasks() { // and the move doesn't alter the sort order. ripe_delayed_tasks.push_back( std::move(const_cast<DelayedTask&>(delayed_task_queue_.top()))); - pending_high_res_task_count_ -= - (delayed_task_queue_.top().task.delay_policy == - subtle::DelayPolicy::kPrecise); - DCHECK_GE(pending_high_res_task_count_, 0); delayed_task_queue_.pop(); } std::tie(process_ripe_tasks_time, std::ignore) = @@ -170,9 +171,34 @@ absl::optional<TimeTicks> DelayedTaskManager::NextScheduledRunTime() const { return delayed_task_queue_.top().task.delayed_run_time; } -bool DelayedTaskManager::HasPendingHighResolutionTasksForTesting() const { +subtle::DelayPolicy DelayedTaskManager::TopTaskDelayPolicyForTesting() const { CheckedAutoLock auto_lock(queue_lock_); - return pending_high_res_task_count_; + return delayed_task_queue_.top().task.delay_policy; +} + +void DelayedTaskManager::Shutdown() { + scoped_refptr<SequencedTaskRunner> service_thread_task_runner; + + { + CheckedAutoLock auto_lock(queue_lock_); + // Prevent delayed tasks from being posted or processed after this. + service_thread_task_runner = service_thread_task_runner_; + } + + if (service_thread_task_runner) { + // Cancel our delayed task on the service thread. This cannot be done from + // ~DelayedTaskManager because the delayed task handle is sequence-affine. + service_thread_task_runner->PostTask( + FROM_HERE, + base::BindOnce( + [](DelayedTaskManager* manager) { + DCHECK_CALLED_ON_VALID_SEQUENCE(manager->sequence_checker_); + manager->delayed_task_handle_.CancelTask(); + }, + // Unretained() is safe because the caller must flush tasks posted + // to the service thread before deleting `this`. + Unretained(this))); + } } std::pair<TimeTicks, subtle::DelayPolicy> DelayedTaskManager:: @@ -184,19 +210,9 @@ std::pair<TimeTicks, subtle::DelayPolicy> DelayedTaskManager:: } const DelayedTask& ripest_delayed_task = delayed_task_queue_.top(); - subtle::DelayPolicy delay_policy = - pending_high_res_task_count_ ? subtle::DelayPolicy::kPrecise - : ripest_delayed_task.task.delay_policy; - - TimeTicks delayed_run_time = ripest_delayed_task.task.delayed_run_time; - if (align_wake_ups_) { - TimeTicks aligned_run_time = - ripest_delayed_task.task.earliest_delayed_run_time().SnappedToNextTick( - TimeTicks(), base::GetTaskLeeway()); - delayed_run_time = std::min( - aligned_run_time, ripest_delayed_task.task.latest_delayed_run_time()); - } - return std::make_pair(delayed_run_time, delay_policy); + subtle::DelayPolicy delay_policy = ripest_delayed_task.task.delay_policy; + return std::make_pair(ripest_delayed_task.task.delayed_run_time, + delay_policy); } void DelayedTaskManager::ScheduleProcessRipeTasksOnServiceThread() { diff --git a/chromium/base/task/thread_pool/delayed_task_manager.h b/chromium/base/task/thread_pool/delayed_task_manager.h index 41961433cc7..a04b4e3f7a7 100644 --- a/chromium/base/task/thread_pool/delayed_task_manager.h +++ b/chromium/base/task/thread_pool/delayed_task_manager.h @@ -61,9 +61,13 @@ class BASE_EXPORT DelayedTaskManager { // Returns the |delayed_run_time| of the next scheduled task, if any. absl::optional<TimeTicks> NextScheduledRunTime() const; - // Returns true if there are any pending tasks in the task source which - // require high resolution timing. - bool HasPendingHighResolutionTasksForTesting() const; + // Returns the DelayPolicy for the next delayed task. + subtle::DelayPolicy TopTaskDelayPolicyForTesting() const; + + // Must be invoked before deleting the delayed task manager. The caller must + // flush tasks posted to the service thread by this before deleting the + // delayed task manager. + void Shutdown(); private: struct DelayedTask { @@ -121,7 +125,7 @@ class BASE_EXPORT DelayedTaskManager { // it is never modified. It is therefore safe to access // |service_thread_task_runner_| without synchronization once it is observed // that it is non-null. - mutable CheckedLock queue_lock_; + mutable CheckedLock queue_lock_{UniversalSuccessor()}; scoped_refptr<SequencedTaskRunner> service_thread_task_runner_; @@ -129,7 +133,6 @@ class BASE_EXPORT DelayedTaskManager { IntrusiveHeap<DelayedTask, std::greater<>> delayed_task_queue_ GUARDED_BY(queue_lock_); - int pending_high_res_task_count_ GUARDED_BY(queue_lock_){0}; bool align_wake_ups_ GUARDED_BY(queue_lock_) = false; diff --git a/chromium/base/task/thread_pool/delayed_task_manager_unittest.cc b/chromium/base/task/thread_pool/delayed_task_manager_unittest.cc index f0d2dd50c11..c9c0dd50798 100644 --- a/chromium/base/task/thread_pool/delayed_task_manager_unittest.cc +++ b/chromium/base/task/thread_pool/delayed_task_manager_unittest.cc @@ -65,7 +65,12 @@ class ThreadPoolDelayedTaskManagerTest : public testing::Test { const ThreadPoolDelayedTaskManagerTest&) = delete; protected: - ThreadPoolDelayedTaskManagerTest() = default; + ThreadPoolDelayedTaskManagerTest() { + // A null clock triggers some assertions. + service_thread_task_runner_->AdvanceMockTickClock(Milliseconds(1)); + task_ = ConstructMockedTask( + mock_callback_, service_thread_task_runner_->NowTicks(), kLongDelay); + } ~ThreadPoolDelayedTaskManagerTest() override = default; const scoped_refptr<TestMockTimeTaskRunner> service_thread_task_runner_ = @@ -73,9 +78,7 @@ class ThreadPoolDelayedTaskManagerTest : public testing::Test { DelayedTaskManager delayed_task_manager_{ service_thread_task_runner_->GetMockTickClock()}; testing::StrictMock<MockCallback> mock_callback_; - Task task_{ConstructMockedTask(mock_callback_, - service_thread_task_runner_->NowTicks(), - kLongDelay)}; + Task task_; }; } // namespace @@ -288,10 +291,12 @@ TEST_F(ThreadPoolDelayedTaskManagerTest, // Send tasks to the DelayedTaskManager. delayed_task_manager_.AddDelayedTask(std::move(task_a), BindOnce(&PostTaskNow), nullptr); - EXPECT_FALSE(delayed_task_manager_.HasPendingHighResolutionTasksForTesting()); + EXPECT_EQ(base::subtle::DelayPolicy::kFlexibleNoSooner, + delayed_task_manager_.TopTaskDelayPolicyForTesting()); delayed_task_manager_.AddDelayedTask(std::move(task_b), BindOnce(&PostTaskNow), nullptr); - EXPECT_TRUE(delayed_task_manager_.HasPendingHighResolutionTasksForTesting()); + EXPECT_EQ(base::subtle::DelayPolicy::kPrecise, + delayed_task_manager_.TopTaskDelayPolicyForTesting()); // The task doesn't run before the delay has completed. service_thread_task_runner_->FastForwardBy(Milliseconds(10) - diff --git a/chromium/base/task/thread_pool/environment_config.cc b/chromium/base/task/thread_pool/environment_config.cc index 4de0c855264..cf497f4fcbe 100644 --- a/chromium/base/task/thread_pool/environment_config.cc +++ b/chromium/base/task/thread_pool/environment_config.cc @@ -46,12 +46,29 @@ bool CanUseBackgroundThreadTypeForWorkerThreadImpl() { return true; } +bool CanUseUtilityThreadTypeForWorkerThreadImpl() { +#if !BUILDFLAG(IS_ANDROID) + // Same as CanUseBackgroundThreadTypeForWorkerThreadImpl() + if (!PlatformThread::CanChangeThreadType(ThreadType::kUtility, + ThreadType::kDefault)) + return false; +#endif // BUILDFLAG(IS_ANDROID) + + return true; +} + } // namespace bool CanUseBackgroundThreadTypeForWorkerThread() { - static const bool can_use_background_priority_for_worker_thread = + static const bool can_use_background_thread_type_for_worker_thread = CanUseBackgroundThreadTypeForWorkerThreadImpl(); - return can_use_background_priority_for_worker_thread; + return can_use_background_thread_type_for_worker_thread; +} + +bool CanUseUtilityThreadTypeForWorkerThread() { + static const bool can_use_utility_thread_type_for_worker_thread = + CanUseUtilityThreadTypeForWorkerThreadImpl(); + return can_use_utility_thread_type_for_worker_thread; } } // namespace internal diff --git a/chromium/base/task/thread_pool/environment_config.h b/chromium/base/task/thread_pool/environment_config.h index 9288d5bc85c..181e1172cb9 100644 --- a/chromium/base/task/thread_pool/environment_config.h +++ b/chromium/base/task/thread_pool/environment_config.h @@ -19,6 +19,8 @@ namespace internal { enum EnvironmentType { FOREGROUND = 0, FOREGROUND_BLOCKING, + UTILITY, + UTILITY_BLOCKING, BACKGROUND, BACKGROUND_BLOCKING, ENVIRONMENT_COUNT // Always last. @@ -38,14 +40,20 @@ struct EnvironmentParams { constexpr EnvironmentParams kEnvironmentParams[] = { {"Foreground", base::ThreadType::kDefault}, {"ForegroundBlocking", base::ThreadType::kDefault}, + {"Utility", base::ThreadType::kUtility}, + {"UtilityBlocking", base::ThreadType::kUtility}, {"Background", base::ThreadType::kBackground}, {"BackgroundBlocking", base::ThreadType::kBackground}, }; // Returns true if this platform supports having WorkerThreads running with a -// background priority. +// background thread type. bool BASE_EXPORT CanUseBackgroundThreadTypeForWorkerThread(); +// Returns true if this platform supports having WorkerThreads running with a +// utility thread type. +bool BASE_EXPORT CanUseUtilityThreadTypeForWorkerThread(); + } // namespace internal } // namespace base diff --git a/chromium/base/task/thread_pool/environment_config_unittest.cc b/chromium/base/task/thread_pool/environment_config_unittest.cc index 5b438958ce2..3a726d07e88 100644 --- a/chromium/base/task/thread_pool/environment_config_unittest.cc +++ b/chromium/base/task/thread_pool/environment_config_unittest.cc @@ -20,6 +20,15 @@ TEST(ThreadPoolEnvironmentConfig, CanUseBackgroundPriorityForWorker) { #else #error Platform doesn't match any block #endif + +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) + EXPECT_TRUE(CanUseUtilityThreadTypeForWorkerThread()); +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_FUCHSIA) || \ + BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_NACL) + EXPECT_FALSE(CanUseUtilityThreadTypeForWorkerThread()); +#else +#error Platform doesn't match any block +#endif } } // namespace internal diff --git a/chromium/base/task/thread_pool/job_task_source.cc b/chromium/base/task/thread_pool/job_task_source.cc index 4376b026378..de92c80d194 100644 --- a/chromium/base/task/thread_pool/job_task_source.cc +++ b/chromium/base/task/thread_pool/job_task_source.cc @@ -12,6 +12,7 @@ #include "base/callback_helpers.h" #include "base/check_op.h" #include "base/memory/ptr_util.h" +#include "base/notreached.h" #include "base/task/common/checked_lock.h" #include "base/task/task_features.h" #include "base/task/thread_pool/pooled_task_runner_delegate.h" @@ -365,7 +366,9 @@ bool JobTaskSource::WillReEnqueue(TimeTicks now, } // This is a no-op. -void JobTaskSource::OnBecomeReady() {} +bool JobTaskSource::OnBecomeReady() { + return false; +} TaskSourceSortKey JobTaskSource::GetSortKey() const { return TaskSourceSortKey(priority_racy(), ready_time_, @@ -378,6 +381,13 @@ TimeTicks JobTaskSource::GetDelayedSortKey() const { return TimeTicks(); } +// This function isn't expected to be called since a job is never delayed. +// However, the class still needs to provide an override. +bool JobTaskSource::HasReadyTasks(TimeTicks now) const { + NOTREACHED(); + return true; +} + Task JobTaskSource::Clear(TaskSource::Transaction* transaction) { Cancel(); // Nothing is cleared since other workers might still racily run tasks. For diff --git a/chromium/base/task/thread_pool/job_task_source.h b/chromium/base/task/thread_pool/job_task_source.h index a7e744eae54..5c431272285 100644 --- a/chromium/base/task/thread_pool/job_task_source.h +++ b/chromium/base/task/thread_pool/job_task_source.h @@ -73,6 +73,7 @@ class BASE_EXPORT JobTaskSource : public TaskSource { size_t GetRemainingConcurrency() const override; TaskSourceSortKey GetSortKey() const override; TimeTicks GetDelayedSortKey() const override; + bool HasReadyTasks(TimeTicks now) const override; bool IsActive() const; size_t GetWorkerCount() const; @@ -195,7 +196,7 @@ class BASE_EXPORT JobTaskSource : public TaskSource { bool DidProcessTask(TaskSource::Transaction* transaction) override; bool WillReEnqueue(TimeTicks now, TaskSource::Transaction* transaction) override; - void OnBecomeReady() override; + bool OnBecomeReady() override; // Synchronizes access to workers state. mutable CheckedLock worker_lock_{UniversalSuccessor()}; diff --git a/chromium/base/task/thread_pool/pooled_sequenced_task_runner.cc b/chromium/base/task/thread_pool/pooled_sequenced_task_runner.cc index 3b7ceb6c788..5b2d9875c14 100644 --- a/chromium/base/task/thread_pool/pooled_sequenced_task_runner.cc +++ b/chromium/base/task/thread_pool/pooled_sequenced_task_runner.cc @@ -31,7 +31,7 @@ bool PooledSequencedTaskRunner::PostDelayedTask(const Location& from_here, } Task task(from_here, std::move(closure), TimeTicks::Now(), delay, - base::GetTaskLeeway()); + GetDefaultTaskLeeway()); // Post the task as part of |sequence_|. return pooled_task_runner_delegate_->PostTaskWithSequence(std::move(task), @@ -50,7 +50,7 @@ bool PooledSequencedTaskRunner::PostDelayedTaskAt( } Task task(from_here, std::move(closure), TimeTicks::Now(), delayed_run_time, - base::GetTaskLeeway(), delay_policy); + GetDefaultTaskLeeway(), delay_policy); // Post the task as part of |sequence_|. return pooled_task_runner_delegate_->PostTaskWithSequence(std::move(task), diff --git a/chromium/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc b/chromium/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc index 76a2b6a04c9..23d88f32ba8 100644 --- a/chromium/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc +++ b/chromium/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc @@ -57,14 +57,23 @@ namespace { // thread pool. bool g_manager_is_alive = false; +bool g_use_utility_thread_group = false; + size_t GetEnvironmentIndexForTraits(const TaskTraits& traits) { const bool is_background = traits.priority() == TaskPriority::BEST_EFFORT && traits.thread_policy() == ThreadPolicy::PREFER_BACKGROUND && CanUseBackgroundThreadTypeForWorkerThread(); - if (traits.may_block() || traits.with_base_sync_primitives()) - return is_background ? BACKGROUND_BLOCKING : FOREGROUND_BLOCKING; - return is_background ? BACKGROUND : FOREGROUND; + const bool is_utility = + !is_background && traits.priority() <= TaskPriority::USER_VISIBLE && + traits.thread_policy() == ThreadPolicy::PREFER_BACKGROUND && + g_use_utility_thread_group; + if (traits.may_block() || traits.with_base_sync_primitives()) { + return is_background ? BACKGROUND_BLOCKING + : is_utility ? UTILITY_BLOCKING + : FOREGROUND_BLOCKING; + } + return is_background ? BACKGROUND : is_utility ? UTILITY : FOREGROUND; } // Allows for checking the PlatformThread::CurrentRef() against a set @@ -148,7 +157,7 @@ class WorkerThreadDelegate : public WorkerThread::Delegate { // |task| will be pushed to |sequence|, and |sequence| will be queued // to |priority_queue_| iff |sequence_should_be_queued| is true. - const bool sequence_should_be_queued = transaction.ShouldBeQueued(); + const bool sequence_should_be_queued = transaction.WillPushImmediateTask(); RegisteredTaskSource task_source; if (sequence_should_be_queued) { task_source = task_tracker_->RegisterTaskSource(sequence); @@ -359,7 +368,8 @@ class WorkerThreadCOMDelegate : public WorkerThreadDelegate { if (task_tracker()->WillPostTask( &pump_message_task, TaskShutdownBehavior::SKIP_ON_SHUTDOWN)) { auto transaction = message_pump_sequence_->BeginTransaction(); - const bool sequence_should_be_queued = transaction.ShouldBeQueued(); + const bool sequence_should_be_queued = + transaction.WillPushImmediateTask(); DCHECK(sequence_should_be_queued) << "GetWorkFromWindowsMessageQueue() does not expect " "queueing of pump tasks."; @@ -417,7 +427,7 @@ class PooledSingleThreadTaskRunnerManager::PooledSingleThreadTaskRunner return false; Task task(from_here, std::move(closure), TimeTicks::Now(), delay, - base::GetTaskLeeway()); + GetDefaultTaskLeeway()); return PostTask(std::move(task)); } @@ -430,7 +440,7 @@ class PooledSingleThreadTaskRunnerManager::PooledSingleThreadTaskRunner return false; Task task(from_here, std::move(closure), TimeTicks::Now(), delayed_run_time, - base::GetTaskLeeway(), delay_policy); + GetDefaultTaskLeeway(), delay_policy); return PostTask(std::move(task)); } @@ -497,7 +507,7 @@ class PooledSingleThreadTaskRunnerManager::PooledSingleThreadTaskRunner } const raw_ptr<PooledSingleThreadTaskRunnerManager> outer_; - const raw_ptr<WorkerThread> worker_; + const raw_ptr<WorkerThread, DanglingUntriaged> worker_; const SingleThreadTaskRunnerThreadMode thread_mode_; const scoped_refptr<Sequence> sequence_; }; @@ -529,6 +539,7 @@ PooledSingleThreadTaskRunnerManager::PooledSingleThreadTaskRunnerManager( PooledSingleThreadTaskRunnerManager::~PooledSingleThreadTaskRunnerManager() { DCHECK(g_manager_is_alive); g_manager_is_alive = false; + g_use_utility_thread_group = false; } void PooledSingleThreadTaskRunnerManager::Start( @@ -541,6 +552,9 @@ void PooledSingleThreadTaskRunnerManager::Start( io_thread_task_runner_ = std::move(io_thread_task_runner); #endif // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA) + g_use_utility_thread_group = CanUseUtilityThreadTypeForWorkerThread() && + FeatureList::IsEnabled(kUseUtilityThreadGroup); + decltype(workers_) workers_to_start; { CheckedAutoLock auto_lock(lock_); @@ -634,10 +648,7 @@ PooledSingleThreadTaskRunnerManager::CreateTaskRunnerImpl( worker_name += "Shared"; worker_name += environment_params.name_suffix; worker = CreateAndRegisterWorkerThread<DelegateType>( - worker_name, thread_mode, - CanUseBackgroundThreadTypeForWorkerThread() - ? environment_params.thread_type_hint - : ThreadType::kDefault); + worker_name, thread_mode, environment_params.thread_type_hint); new_worker = true; } started = started_; @@ -717,7 +728,7 @@ PooledSingleThreadTaskRunnerManager::CreateAndRegisterWorkerThread( CreateWorkerThreadDelegate<DelegateType>(name, id, thread_mode); WorkerThreadDelegate* delegate_raw = delegate.get(); scoped_refptr<WorkerThread> worker = MakeRefCounted<WorkerThread>( - thread_type_hint, std::move(delegate), task_tracker_); + thread_type_hint, std::move(delegate), task_tracker_, workers_.size()); delegate_raw->set_worker(worker.get()); workers_.emplace_back(std::move(worker)); return workers_.back().get(); diff --git a/chromium/base/task/thread_pool/pooled_single_thread_task_runner_manager_unittest.cc b/chromium/base/task/thread_pool/pooled_single_thread_task_runner_manager_unittest.cc index 95b36d51e23..136ea27dae3 100644 --- a/chromium/base/task/thread_pool/pooled_single_thread_task_runner_manager_unittest.cc +++ b/chromium/base/task/thread_pool/pooled_single_thread_task_runner_manager_unittest.cc @@ -18,6 +18,7 @@ #include "base/task/thread_pool/test_utils.h" #include "base/test/bind.h" #include "base/test/gtest_util.h" +#include "base/test/scoped_feature_list.h" #include "base/test/test_timeouts.h" #include "base/test/test_waitable_event.h" #include "base/threading/platform_thread.h" @@ -61,6 +62,7 @@ class PooledSingleThreadTaskRunnerManagerTest : public testing::Test { void TearDown() override { if (single_thread_task_runner_manager_) TearDownSingleThreadTaskRunnerManager(); + delayed_task_manager_.Shutdown(); service_thread_.Stop(); } @@ -224,9 +226,15 @@ namespace { class PooledSingleThreadTaskRunnerManagerCommonTest : public PooledSingleThreadTaskRunnerManagerTest, - public ::testing::WithParamInterface<SingleThreadTaskRunnerThreadMode> { + public ::testing::WithParamInterface< + std::tuple<SingleThreadTaskRunnerThreadMode, + bool /* enable_utility_threads */>> { public: - PooledSingleThreadTaskRunnerManagerCommonTest() = default; + PooledSingleThreadTaskRunnerManagerCommonTest() { + if (std::get<1>(GetParam())) { + feature_list_.InitWithFeatures({kUseUtilityThreadGroup}, {}); + } + } PooledSingleThreadTaskRunnerManagerCommonTest( const PooledSingleThreadTaskRunnerManagerCommonTest&) = delete; PooledSingleThreadTaskRunnerManagerCommonTest& operator=( @@ -235,8 +243,17 @@ class PooledSingleThreadTaskRunnerManagerCommonTest scoped_refptr<SingleThreadTaskRunner> CreateTaskRunner( TaskTraits traits = {}) { return single_thread_task_runner_manager_->CreateSingleThreadTaskRunner( - traits, GetParam()); + traits, GetSingleThreadTaskRunnerThreadMode()); + } + + SingleThreadTaskRunnerThreadMode GetSingleThreadTaskRunnerThreadMode() const { + return std::get<0>(GetParam()); } + + protected: + const bool use_utility_thread_group_ = + CanUseUtilityThreadTypeForWorkerThread() && std::get<1>(GetParam()); + base::test::ScopedFeatureList feature_list_; }; } // namespace @@ -248,15 +265,18 @@ TEST_P(PooledSingleThreadTaskRunnerManagerCommonTest, ThreadTypeSetCorrectly) { } test_cases[] = { {{TaskPriority::BEST_EFFORT}, CanUseBackgroundThreadTypeForWorkerThread() ? ThreadType::kBackground + : use_utility_thread_group_ ? ThreadType::kUtility : ThreadType::kDefault}, {{TaskPriority::BEST_EFFORT, ThreadPolicy::PREFER_BACKGROUND}, CanUseBackgroundThreadTypeForWorkerThread() ? ThreadType::kBackground + : use_utility_thread_group_ ? ThreadType::kUtility : ThreadType::kDefault}, {{TaskPriority::BEST_EFFORT, ThreadPolicy::MUST_USE_FOREGROUND}, ThreadType::kDefault}, - {{TaskPriority::USER_VISIBLE}, ThreadType::kDefault}, + {{TaskPriority::USER_VISIBLE}, + use_utility_thread_group_ ? ThreadType::kUtility : ThreadType::kDefault}, {{TaskPriority::USER_VISIBLE, ThreadPolicy::PREFER_BACKGROUND}, - ThreadType::kDefault}, + use_utility_thread_group_ ? ThreadType::kUtility : ThreadType::kDefault}, {{TaskPriority::USER_VISIBLE, ThreadPolicy::MUST_USE_FOREGROUND}, ThreadType::kDefault}, {{TaskPriority::USER_BLOCKING}, ThreadType::kDefault}, @@ -282,14 +302,20 @@ TEST_P(PooledSingleThreadTaskRunnerManagerCommonTest, ThreadTypeSetCorrectly) { TEST_P(PooledSingleThreadTaskRunnerManagerCommonTest, ThreadNamesSet) { const std::string maybe_shared( - GetParam() == SingleThreadTaskRunnerThreadMode::DEDICATED ? "" - : "Shared"); + GetSingleThreadTaskRunnerThreadMode() == + SingleThreadTaskRunnerThreadMode::DEDICATED + ? "" + : "Shared"); const std::string background = "^ThreadPoolSingleThread" + maybe_shared + "Background\\d+$"; + const std::string utility = + "^ThreadPoolSingleThread" + maybe_shared + "Utility\\d+$"; const std::string foreground = "^ThreadPoolSingleThread" + maybe_shared + "Foreground\\d+$"; const std::string background_blocking = "^ThreadPoolSingleThread" + maybe_shared + "BackgroundBlocking\\d+$"; + const std::string utility_blocking = + "^ThreadPoolSingleThread" + maybe_shared + "UtilityBlocking\\d+$"; const std::string foreground_blocking = "^ThreadPoolSingleThread" + maybe_shared + "ForegroundBlocking\\d+$"; @@ -299,14 +325,19 @@ TEST_P(PooledSingleThreadTaskRunnerManagerCommonTest, ThreadNamesSet) { } test_cases[] = { // Non-MayBlock() {{TaskPriority::BEST_EFFORT}, - CanUseBackgroundThreadTypeForWorkerThread() ? background : foreground}, + CanUseBackgroundThreadTypeForWorkerThread() ? background + : use_utility_thread_group_ ? utility + : foreground}, {{TaskPriority::BEST_EFFORT, ThreadPolicy::PREFER_BACKGROUND}, - CanUseBackgroundThreadTypeForWorkerThread() ? background : foreground}, + CanUseBackgroundThreadTypeForWorkerThread() ? background + : use_utility_thread_group_ ? utility + : foreground}, {{TaskPriority::BEST_EFFORT, ThreadPolicy::MUST_USE_FOREGROUND}, foreground}, - {{TaskPriority::USER_VISIBLE}, foreground}, + {{TaskPriority::USER_VISIBLE}, + use_utility_thread_group_ ? utility : foreground}, {{TaskPriority::USER_VISIBLE, ThreadPolicy::PREFER_BACKGROUND}, - foreground}, + use_utility_thread_group_ ? utility : foreground}, {{TaskPriority::USER_VISIBLE, ThreadPolicy::MUST_USE_FOREGROUND}, foreground}, {{TaskPriority::USER_BLOCKING}, foreground}, @@ -318,17 +349,20 @@ TEST_P(PooledSingleThreadTaskRunnerManagerCommonTest, ThreadNamesSet) { // MayBlock() {{TaskPriority::BEST_EFFORT, MayBlock()}, CanUseBackgroundThreadTypeForWorkerThread() ? background_blocking + : use_utility_thread_group_ ? utility_blocking : foreground_blocking}, {{TaskPriority::BEST_EFFORT, ThreadPolicy::PREFER_BACKGROUND, MayBlock()}, CanUseBackgroundThreadTypeForWorkerThread() ? background_blocking + : use_utility_thread_group_ ? utility_blocking : foreground_blocking}, {{TaskPriority::BEST_EFFORT, ThreadPolicy::MUST_USE_FOREGROUND, MayBlock()}, foreground_blocking}, - {{TaskPriority::USER_VISIBLE, MayBlock()}, foreground_blocking}, + {{TaskPriority::USER_VISIBLE, MayBlock()}, + use_utility_thread_group_ ? utility_blocking : foreground_blocking}, {{TaskPriority::USER_VISIBLE, ThreadPolicy::PREFER_BACKGROUND, MayBlock()}, - foreground_blocking}, + use_utility_thread_group_ ? utility_blocking : foreground_blocking}, {{TaskPriority::USER_VISIBLE, ThreadPolicy::MUST_USE_FOREGROUND, MayBlock()}, @@ -426,8 +460,10 @@ TEST_P(PooledSingleThreadTaskRunnerManagerCommonTest, CanRunPolicyLoad) { INSTANTIATE_TEST_SUITE_P( SharedAndDedicated, PooledSingleThreadTaskRunnerManagerCommonTest, - ::testing::Values(SingleThreadTaskRunnerThreadMode::SHARED, - SingleThreadTaskRunnerThreadMode::DEDICATED)); + ::testing::Combine( + ::testing::Values(SingleThreadTaskRunnerThreadMode::SHARED, + SingleThreadTaskRunnerThreadMode::DEDICATED), + ::testing::Values(false, true))); namespace { @@ -537,7 +573,8 @@ TEST_F(PooledSingleThreadTaskRunnerManagerJoinTest, TEST_P(PooledSingleThreadTaskRunnerManagerCommonTest, COMSTAInitialized) { scoped_refptr<SingleThreadTaskRunner> com_task_runner = single_thread_task_runner_manager_->CreateCOMSTATaskRunner( - {TaskShutdownBehavior::BLOCK_SHUTDOWN}, GetParam()); + {TaskShutdownBehavior::BLOCK_SHUTDOWN}, + GetSingleThreadTaskRunnerThreadMode()); com_task_runner->PostTask(FROM_HERE, BindOnce(&win::AssertComApartmentType, win::ComApartmentType::STA)); diff --git a/chromium/base/task/thread_pool/priority_queue.cc b/chromium/base/task/thread_pool/priority_queue.cc index 8e53ed40445..47e3d4ce8f1 100644 --- a/chromium/base/task/thread_pool/priority_queue.cc +++ b/chromium/base/task/thread_pool/priority_queue.cc @@ -8,7 +8,7 @@ #include "base/check_op.h" #include "base/memory/ptr_util.h" -#include "base/stl_util.h" +#include "base/types/cxx23_to_underlying.h" namespace base { namespace internal { diff --git a/chromium/base/task/thread_pool/priority_queue.h b/chromium/base/task/thread_pool/priority_queue.h index f6a39c71001..ebafb54a882 100644 --- a/chromium/base/task/thread_pool/priority_queue.h +++ b/chromium/base/task/thread_pool/priority_queue.h @@ -10,10 +10,10 @@ #include "base/base_export.h" #include "base/containers/intrusive_heap.h" -#include "base/stl_util.h" #include "base/task/common/checked_lock.h" #include "base/task/thread_pool/task_source.h" #include "base/task/thread_pool/task_source_sort_key.h" +#include "base/types/cxx23_to_underlying.h" namespace base { namespace internal { diff --git a/chromium/base/task/thread_pool/priority_queue_unittest.cc b/chromium/base/task/thread_pool/priority_queue_unittest.cc index aef842cd46e..46d461854b1 100644 --- a/chromium/base/task/thread_pool/priority_queue_unittest.cc +++ b/chromium/base/task/thread_pool/priority_queue_unittest.cc @@ -43,7 +43,9 @@ class PriorityQueueWithSequencesTest : public testing::Test { task_environment.FastForwardBy(Microseconds(1)); scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>( traits, nullptr, TaskSourceExecutionMode::kParallel); - sequence->BeginTransaction().PushImmediateTask( + auto transaction = sequence->BeginTransaction(); + transaction.WillPushImmediateTask(); + transaction.PushImmediateTask( Task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta())); return sequence; } diff --git a/chromium/base/task/thread_pool/sequence.cc b/chromium/base/task/thread_pool/sequence.cc index 0730f702907..0efc4f534e4 100644 --- a/chromium/base/task/thread_pool/sequence.cc +++ b/chromium/base/task/thread_pool/sequence.cc @@ -17,6 +17,28 @@ namespace base { namespace internal { +namespace { + +// Asserts that a lock is acquired and annotates the scope such that +// base/thread_annotations.h can recognize that the lock is acquired. +class SCOPED_LOCKABLE AnnotateLockAcquired { + public: + explicit AnnotateLockAcquired(const CheckedLock& lock) + EXCLUSIVE_LOCK_FUNCTION(lock) + : acquired_lock_(lock) { + acquired_lock_->AssertAcquired(); + } + + ~AnnotateLockAcquired() UNLOCK_FUNCTION() { + acquired_lock_->AssertAcquired(); + } + + private: + const raw_ref<const CheckedLock> acquired_lock_; +}; + +} // namespace + Sequence::Transaction::Transaction(Sequence* sequence) : TaskSource::Transaction(sequence) {} @@ -24,50 +46,37 @@ Sequence::Transaction::Transaction(Sequence::Transaction&& other) = default; Sequence::Transaction::~Transaction() = default; -bool Sequence::Transaction::ShouldBeQueued() const { - // A sequence should be queued to the immediate queue after receiving a new - // immediate Task, or queued to or updated in the delayed queue after - // receiving a new delayed Task, if it's not already in the immediate queue - // and the pool is not running any task from it. WillRunTask() can racily - // modify |current_location_|, but always from |kImmediateQueue| to - // |kInWorker|. In that case, ShouldBeQueued() returns false whether - // WillRunTask() runs immediately before or after. - // When pushing a delayed task, a sequence can become ready at any time, - // triggering OnBecomeReady() which racily modifies |current_location_| - // from kDelayedQueue to kImmediateQueue. In that case this function may - // return true which immediately becomes incorrect. This race is resolved - // outside of this class. See my comment on ShouldBeQueued() in the header - // file. - auto current_location = - sequence()->current_location_.load(std::memory_order_relaxed); - if (current_location == Sequence::SequenceLocation::kImmediateQueue || - current_location == Sequence::SequenceLocation::kInWorker) { +bool Sequence::DelayedSortKeyWillChange(const Task& delayed_task) const { + AnnotateLockAcquired annotate(lock_); + // If sequence has already been picked up by a worker or moved, no need to + // proceed further here. + if (is_immediate_.load(std::memory_order_relaxed)) return false; - } - - return true; -} -bool Sequence::Transaction::TopDelayedTaskWillChange(Task& delayed_task) const { - if (sequence()->IsEmpty()) + if (IsEmpty()) return true; + return delayed_task.latest_delayed_run_time() < - sequence()->delayed_queue_.top().latest_delayed_run_time(); + delayed_queue_.top().latest_delayed_run_time(); +} + +bool Sequence::Transaction::WillPushImmediateTask() { + AnnotateLockAcquired annotate(sequence()->lock_); + bool was_immediate = + sequence()->is_immediate_.exchange(true, std::memory_order_relaxed); + return !was_immediate; } void Sequence::Transaction::PushImmediateTask(Task task) { + AnnotateLockAcquired annotate(sequence()->lock_); // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167 // for details. CHECK(task.task); DCHECK(!task.queue_time.is_null()); + DCHECK(sequence()->is_immediate_.load(std::memory_order_relaxed)); - auto current_location = - sequence()->current_location_.load(std::memory_order_relaxed); - bool was_unretained = - sequence()->IsEmpty() && - current_location != Sequence::SequenceLocation::kInWorker; + bool was_unretained = sequence()->IsEmpty() && !sequence()->has_worker_; bool queue_was_empty = sequence()->queue_.empty(); - task.task = sequence()->traits_.shutdown_behavior() == TaskShutdownBehavior::BLOCK_SHUTDOWN ? MakeCriticalClosure( @@ -77,16 +86,8 @@ void Sequence::Transaction::PushImmediateTask(Task task) { sequence()->queue_.push(std::move(task)); - if (queue_was_empty) { - sequence()->ready_time_.store(sequence()->GetNextReadyTime(), - std::memory_order_relaxed); - } - - if (current_location == Sequence::SequenceLocation::kDelayedQueue || - current_location == Sequence::SequenceLocation::kNone) { - sequence()->current_location_.store( - Sequence::SequenceLocation::kImmediateQueue, std::memory_order_relaxed); - } + if (queue_was_empty) + sequence()->UpdateReadyTimes(); // AddRef() matched by manual Release() when the sequence has no more tasks // to run (in DidProcessTask() or Clear()). @@ -94,19 +95,16 @@ void Sequence::Transaction::PushImmediateTask(Task task) { sequence()->task_runner()->AddRef(); } -void Sequence::Transaction::PushDelayedTask(Task task) { +bool Sequence::Transaction::PushDelayedTask(Task task) { + AnnotateLockAcquired annotate(sequence()->lock_); // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167 // for details. CHECK(task.task); DCHECK(!task.queue_time.is_null()); DCHECK(!task.delayed_run_time.is_null()); - auto current_location = - sequence()->current_location_.load(std::memory_order_relaxed); - bool was_unretained = - sequence()->IsEmpty() && - current_location != Sequence::SequenceLocation::kInWorker; - + bool top_will_change = sequence()->DelayedSortKeyWillChange(task); + bool was_empty = sequence()->IsEmpty(); task.task = sequence()->traits_.shutdown_behavior() == TaskShutdownBehavior::BLOCK_SHUTDOWN @@ -115,20 +113,15 @@ void Sequence::Transaction::PushDelayedTask(Task task) { sequence()->delayed_queue_.insert(std::move(task)); - if (sequence()->queue_.empty()) { - sequence()->ready_time_.store(sequence()->GetNextReadyTime(), - std::memory_order_relaxed); - } - - auto expected_location = Sequence::SequenceLocation::kNone; - sequence()->current_location_.compare_exchange_strong( - expected_location, Sequence::SequenceLocation::kDelayedQueue, - std::memory_order_relaxed); + if (sequence()->queue_.empty()) + sequence()->UpdateReadyTimes(); // AddRef() matched by manual Release() when the sequence has no more tasks // to run (in DidProcessTask() or Clear()). - if (was_unretained && sequence()->task_runner()) + if (was_empty && !sequence()->has_worker_ && sequence()->task_runner()) sequence()->task_runner()->AddRef(); + + return top_will_change; } // Delayed tasks are ordered by latest_delayed_run_time(). The top task may @@ -145,35 +138,21 @@ bool Sequence::DelayedTaskGreater::operator()(const Task& lhs, TaskSource::RunStatus Sequence::WillRunTask() { // There should never be a second call to WillRunTask() before DidProcessTask // since the RunStatus is always marked a saturated. + DCHECK(!has_worker_); - DCHECK_EQ(current_location_.load(std::memory_order_relaxed), - Sequence::SequenceLocation::kImmediateQueue); - - // It's ok to access |current_location_| outside of a Transaction since + // It's ok to access |has_worker_| outside of a Transaction since // WillRunTask() is externally synchronized, always called in sequence with - // OnBecomeReady(), TakeTask(), WillReEnqueue() and DidProcessTask() and only - // called if sequence is in immediate queue. Even though it can get racy with - // ShouldBeQueued()/PushImmediateTask()/PushDelayedTask(), the behavior of - // each function is not affected as explained in ShouldBeQueued(). - current_location_.store(Sequence::SequenceLocation::kInWorker, - std::memory_order_relaxed); - + // TakeTask() and DidProcessTask() and only called if HasReadyTasks(), which + // means it won't race with Push[Immediate/Delayed]Task(). + has_worker_ = true; return RunStatus::kAllowedSaturated; } -void Sequence::OnBecomeReady() { - // This should always be called from a worker thread at a time and it will be - // called only before WillRunTask(). - DCHECK(current_location_.load(std::memory_order_relaxed) == - Sequence::SequenceLocation::kDelayedQueue); - - // It's ok to access |current_location_| outside of a Transaction since - // OnBecomeReady() is externally synchronized and always called in sequence - // with WillRunTask(). This can get racy with - // ShouldBeQueued()/PushDelayedTask(). See comment in - // ShouldBeQueued() to see how races with this function are resolved. - current_location_.store(Sequence::SequenceLocation::kImmediateQueue, - std::memory_order_relaxed); +bool Sequence::OnBecomeReady() { + DCHECK(!has_worker_); + // std::memory_order_relaxed is sufficient because no other state is + // synchronized with |is_immediate_| outside of |lock_|. + return !is_immediate_.exchange(true, std::memory_order_relaxed); } size_t Sequence::GetRemainingConcurrency() const { @@ -202,44 +181,57 @@ Task Sequence::TakeEarliestTask() { return delayed_queue_.take_top(); } -TimeTicks Sequence::GetNextReadyTime() { - if (queue_.empty()) - return delayed_queue_.top().latest_delayed_run_time(); - - if (delayed_queue_.empty()) - return queue_.front().queue_time; +void Sequence::UpdateReadyTimes() { + DCHECK(!IsEmpty()); + if (queue_.empty()) { + latest_ready_time_.store(delayed_queue_.top().latest_delayed_run_time(), + std::memory_order_relaxed); + earliest_ready_time_.store(delayed_queue_.top().earliest_delayed_run_time(), + std::memory_order_relaxed); + return; + } - return std::min(queue_.front().queue_time, - delayed_queue_.top().latest_delayed_run_time()); + if (delayed_queue_.empty()) { + latest_ready_time_.store(queue_.front().queue_time, + std::memory_order_relaxed); + } else { + latest_ready_time_.store( + std::min(queue_.front().queue_time, + delayed_queue_.top().latest_delayed_run_time()), + std::memory_order_relaxed); + } + earliest_ready_time_.store(TimeTicks(), std::memory_order_relaxed); } Task Sequence::TakeTask(TaskSource::Transaction* transaction) { CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_); + AnnotateLockAcquired annotate(lock_); - DCHECK(current_location_.load(std::memory_order_relaxed) == - Sequence::SequenceLocation::kInWorker); + DCHECK(has_worker_); + DCHECK(is_immediate_.load(std::memory_order_relaxed)); DCHECK(!queue_.empty() || !delayed_queue_.empty()); auto next_task = TakeEarliestTask(); if (!IsEmpty()) - ready_time_.store(GetNextReadyTime(), std::memory_order_relaxed); + UpdateReadyTimes(); return next_task; } bool Sequence::DidProcessTask(TaskSource::Transaction* transaction) { CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_); + AnnotateLockAcquired annotate(lock_); + // There should never be a call to DidProcessTask without an associated // WillRunTask(). - DCHECK(current_location_.load(std::memory_order_relaxed) == - Sequence::SequenceLocation::kInWorker); + DCHECK(has_worker_); + has_worker_ = false; // See comment on TaskSource::task_runner_ for lifetime management details. if (IsEmpty()) { + is_immediate_.store(false, std::memory_order_relaxed); ReleaseTaskRunner(); - current_location_.store(Sequence::SequenceLocation::kNone, - std::memory_order_relaxed); return false; } @@ -252,35 +244,22 @@ bool Sequence::DidProcessTask(TaskSource::Transaction* transaction) { bool Sequence::WillReEnqueue(TimeTicks now, TaskSource::Transaction* transaction) { CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_); + AnnotateLockAcquired annotate(lock_); + // This should always be called from a worker thread and it will be // called after DidProcessTask(). - DCHECK(current_location_.load(std::memory_order_relaxed) == - Sequence::SequenceLocation::kInWorker); + DCHECK(is_immediate_.load(std::memory_order_relaxed)); bool has_ready_tasks = HasReadyTasks(now); - if (has_ready_tasks) { - current_location_.store(Sequence::SequenceLocation::kImmediateQueue, - std::memory_order_relaxed); - } else { - current_location_.store(Sequence::SequenceLocation::kDelayedQueue, - std::memory_order_relaxed); - } + if (!has_ready_tasks) + is_immediate_.store(false, std::memory_order_relaxed); return has_ready_tasks; } bool Sequence::HasReadyTasks(TimeTicks now) const { - return HasRipeDelayedTasks(now) || HasImmediateTasks(); -} - -bool Sequence::HasRipeDelayedTasks(TimeTicks now) const { - if (delayed_queue_.empty()) - return false; - - if (!delayed_queue_.top().task.MaybeValid()) - return true; - - return delayed_queue_.top().earliest_delayed_run_time() <= now; + return now >= TS_UNCHECKED_READ(earliest_ready_time_) + .load(std::memory_order_relaxed); } bool Sequence::HasImmediateTasks() const { @@ -288,19 +267,21 @@ bool Sequence::HasImmediateTasks() const { } TaskSourceSortKey Sequence::GetSortKey() const { - return TaskSourceSortKey(priority_racy(), - ready_time_.load(std::memory_order_relaxed)); + return TaskSourceSortKey( + priority_racy(), + TS_UNCHECKED_READ(latest_ready_time_).load(std::memory_order_relaxed)); } TimeTicks Sequence::GetDelayedSortKey() const { - return GetReadyTime(); + return TS_UNCHECKED_READ(latest_ready_time_).load(std::memory_order_relaxed); } Task Sequence::Clear(TaskSource::Transaction* transaction) { CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_); + AnnotateLockAcquired annotate(lock_); + // See comment on TaskSource::task_runner_ for lifetime management details. - if (!IsEmpty() && current_location_.load(std::memory_order_relaxed) != - Sequence::SequenceLocation::kInWorker) { + if (!IsEmpty() && !has_worker_) { ReleaseTaskRunner(); } @@ -342,17 +323,9 @@ ExecutionEnvironment Sequence::GetExecutionEnvironment() { return {token_, &sequence_local_storage_}; } -Sequence::SequenceLocation Sequence::GetCurrentLocationForTesting() { - return current_location_.load(std::memory_order_relaxed); -} - bool Sequence::IsEmpty() const { return queue_.empty() && delayed_queue_.empty(); } -TimeTicks Sequence::GetReadyTime() const { - return ready_time_.load(std::memory_order_relaxed); -} - } // namespace internal } // namespace base diff --git a/chromium/base/task/thread_pool/sequence.h b/chromium/base/task/thread_pool/sequence.h index 7f0f3032649..decdbdfb1d2 100644 --- a/chromium/base/task/thread_pool/sequence.h +++ b/chromium/base/task/thread_pool/sequence.h @@ -16,6 +16,7 @@ #include "base/task/thread_pool/task.h" #include "base/task/thread_pool/task_source.h" #include "base/task/thread_pool/task_source_sort_key.h" +#include "base/thread_annotations.h" #include "base/threading/sequence_local_storage_map.h" namespace base { @@ -24,8 +25,6 @@ namespace internal { // A Sequence is intended to hold delayed tasks and immediate tasks. // Delayed tasks are held in a prority_queue until they are ripe and // immediate tasks in a simple fifo queue. -// Sequence::PushTask is responsible for putting a task into the right -// queue depending on its nature. // When Sequence::TakeTask is called, we select the next appropriate task // from both queues and return it. // Each queue holds slots each containing up to a single Task that must be @@ -58,26 +57,19 @@ class BASE_EXPORT Sequence : public TaskSource { Transaction& operator=(const Transaction&) = delete; ~Transaction(); - // Returns true if the sequence would need to be queued in the - // immediate/delayed queue after receiving a new immediate/delayed Task. - // Thread-safe but the returned value may immediately be obsolete when - // pushing a delayed task since a sequence can become ready at any time; - // therefore it must be externally synchronized to prevent races against - // OnBecomeReady(). - [[nodiscard]] bool ShouldBeQueued() const; + // Returns true if the sequence must be added to the immediate queue after + // receiving a new immediate Task in order to be scheduled. If the caller + // doesn't want the sequence to be scheduled, it may not add the sequence to + // the immediate queue even if this returns true. + bool WillPushImmediateTask(); - // Returns true if the task to be posted will change the sequence - // delayed_queue top. - bool TopDelayedTaskWillChange(Task& delayed_task) const; - - // Adds immediate |task| to the end of this sequence. This must only - // be called after invoking ShouldBeQueued(). + // Adds immediate |task| to the end of this sequence. void PushImmediateTask(Task task); - // Adds a delayed |task| in this sequence to be prioritized based on it's - // delayed run time. This must only be called after invoking - // TopDelayedTaskWillChange()/ShouldBeQueued(). - void PushDelayedTask(Task task); + // Adds a delayed |task| in this sequence, and returns true if the sequence + // needs to be re-enqueued in the delayed queue as a result of this + // sequence's delayed sort key changing. + bool PushDelayedTask(Task task); Sequence* sequence() const { return static_cast<Sequence*>(task_source()); } @@ -87,19 +79,6 @@ class BASE_EXPORT Sequence : public TaskSource { explicit Transaction(Sequence* sequence); }; - // This indicates where a sequence is stored, used by Sequence to keep track - // of its status. - enum class SequenceLocation { - // Sequence is not present in any queue. - kNone, - // Sequence is present in queue of immediate sequences. - kImmediateQueue, - // Sequence is present in queue of delayed sequences. - kDelayedQueue, - // Sequence is being run by a worker. - kInWorker, - }; - // |traits| is metadata that applies to all Tasks in the Sequence. // |task_runner| is a reference to the TaskRunner feeding this TaskSource. // |task_runner| can be nullptr only for tasks with no TaskRunner, in which @@ -128,9 +107,13 @@ class BASE_EXPORT Sequence : public TaskSource { return &sequence_local_storage_; } - SequenceLocation GetCurrentLocationForTesting(); + bool OnBecomeReady() override; - void OnBecomeReady() override; + bool has_worker_for_testing() const NO_THREAD_SAFETY_ANALYSIS { + return has_worker_; + } + bool is_immediate_for_testing() const { return is_immediate_; } + bool IsEmptyForTesting() const NO_THREAD_SAFETY_ANALYSIS { return IsEmpty(); } private: ~Sequence() override; @@ -147,30 +130,29 @@ class BASE_EXPORT Sequence : public TaskSource { bool WillReEnqueue(TimeTicks now, TaskSource::Transaction* transaction) override; + // Returns true if the delayed task to be posted will cause the delayed sort + // key to change. + bool DelayedSortKeyWillChange(const Task& delayed_task) const; + // Selects the earliest task to run, either from immediate or // delayed queue and return it. // Expects this sequence to have at least one task that can run // immediately. - Task TakeEarliestTask(); + Task TakeEarliestTask() EXCLUSIVE_LOCKS_REQUIRED(lock_); // Get and return next task from immediate queue - Task TakeNextImmediateTask(); + Task TakeNextImmediateTask() EXCLUSIVE_LOCKS_REQUIRED(lock_); - // Determine next ready time and set ready time to it - TimeTicks GetNextReadyTime(); + // Update the next earliest/latest ready time. + void UpdateReadyTimes() EXCLUSIVE_LOCKS_REQUIRED(lock_); // Returns true if there are immediate tasks - bool HasImmediateTasks() const; - - // Returns true if there are tasks ripe for execution in the delayed queue - bool HasRipeDelayedTasks(TimeTicks now) const; + bool HasImmediateTasks() const EXCLUSIVE_LOCKS_REQUIRED(lock_); // Returns true if tasks ready to be executed - bool HasReadyTasks(TimeTicks now) const; - - bool IsEmpty() const; + bool HasReadyTasks(TimeTicks now) const override; - TimeTicks GetReadyTime() const; + bool IsEmpty() const EXCLUSIVE_LOCKS_REQUIRED(lock_); // Releases reference to TaskRunner. void ReleaseTaskRunner(); @@ -178,18 +160,33 @@ class BASE_EXPORT Sequence : public TaskSource { const SequenceToken token_ = SequenceToken::Create(); // Queues of tasks to execute. - base::queue<Task> queue_; - base::IntrusiveHeap<Task, DelayedTaskGreater> delayed_queue_; - - std::atomic<TimeTicks> ready_time_{TimeTicks()}; + base::queue<Task> queue_ GUARDED_BY(lock_); + base::IntrusiveHeap<Task, DelayedTaskGreater> delayed_queue_ + GUARDED_BY(lock_); + + // Caches the latest/earliest ready time for atomic access. Writes are + // protected by |lock_|, but allows atomic reads outside of |lock_|. If this + // sequence is empty, these are in an unknown state and shouldn't be read. + + // Minimum of latest_delayed_run_time() of next delayed task if any, and + // |queue_time| of next immediate task if any. + std::atomic<TimeTicks> latest_ready_time_ GUARDED_BY(lock_){TimeTicks()}; + // is_null() if there is an immediate task, or earliest_delayed_run_time() of + // next delayed task otherwise. + std::atomic<TimeTicks> earliest_ready_time_ GUARDED_BY(lock_){TimeTicks()}; + + // True if a worker is currently associated with a Task from this Sequence. + bool has_worker_ = false; + + // True if the sequence has ready tasks and requested to be queued as such + // through WillPushImmediateTask() or OnBecomeReady(). Reset to false once all + // ready tasks are done being processed and either DidProcessTask() or + // WillReEnqueue() returned false. Normally, |is_immediate_| is protected by + // |lock_|, except in OnBecomeReady() hence the use of atomics. + std::atomic_bool is_immediate_{false}; // Holds data stored through the SequenceLocalStorageSlot API. SequenceLocalStorageMap sequence_local_storage_; - - // This member will hold the current location of the sequence at any time. - // At instantiation, the sequence is not put in any queue yet so the - // sequence location is set to |kNone|. - std::atomic<SequenceLocation> current_location_{SequenceLocation::kNone}; }; } // namespace internal diff --git a/chromium/base/task/thread_pool/sequence_unittest.cc b/chromium/base/task/thread_pool/sequence_unittest.cc index 6f352cb46b4..2588415df03 100644 --- a/chromium/base/task/thread_pool/sequence_unittest.cc +++ b/chromium/base/task/thread_pool/sequence_unittest.cc @@ -58,17 +58,17 @@ TEST(ThreadPoolSequenceTest, PushTakeRemove) { Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); // Push task A in the sequence. PushImmediateTask() should return true since - // it's the first task-> - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + // it's the first task. + EXPECT_TRUE(sequence_transaction.WillPushImmediateTask()); sequence_transaction.PushImmediateTask(CreateTask(&mock_task_a)); // Push task B, C and D in the sequence. PushImmediateTask() should return // false since there is already a task in a sequence. - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + EXPECT_FALSE(sequence_transaction.WillPushImmediateTask()); sequence_transaction.PushImmediateTask(CreateTask(&mock_task_b)); - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + EXPECT_FALSE(sequence_transaction.WillPushImmediateTask()); sequence_transaction.PushImmediateTask(CreateTask(&mock_task_c)); - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + EXPECT_FALSE(sequence_transaction.WillPushImmediateTask()); sequence_transaction.PushImmediateTask(CreateTask(&mock_task_d)); // Take the task in front of the sequence. It should be task A. @@ -85,8 +85,8 @@ TEST(ThreadPoolSequenceTest, PushTakeRemove) { EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), &sequence_transaction)); - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); registered_task_source.WillRunTask(); + EXPECT_TRUE(sequence->has_worker_for_testing()); task = registered_task_source.TakeTask(&sequence_transaction); ExpectMockTask(&mock_task_b, &task.value()); EXPECT_FALSE(task->queue_time.is_null()); @@ -96,8 +96,8 @@ TEST(ThreadPoolSequenceTest, PushTakeRemove) { EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), &sequence_transaction)); - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); registered_task_source.WillRunTask(); + EXPECT_TRUE(sequence->has_worker_for_testing()); task = registered_task_source.TakeTask(&sequence_transaction); ExpectMockTask(&mock_task_c, &task.value()); EXPECT_FALSE(task->queue_time.is_null()); @@ -106,13 +106,15 @@ TEST(ThreadPoolSequenceTest, PushTakeRemove) { EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), &sequence_transaction)); + EXPECT_FALSE(sequence->has_worker_for_testing()); // Push task E in the sequence. - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + EXPECT_FALSE(sequence_transaction.WillPushImmediateTask()); sequence_transaction.PushImmediateTask(CreateTask(&mock_task_e)); // Task D should be in front. registered_task_source.WillRunTask(); + EXPECT_TRUE(sequence->has_worker_for_testing()); task = registered_task_source.TakeTask(&sequence_transaction); ExpectMockTask(&mock_task_d, &task.value()); EXPECT_FALSE(task->queue_time.is_null()); @@ -121,7 +123,6 @@ TEST(ThreadPoolSequenceTest, PushTakeRemove) { EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), &sequence_transaction)); - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); registered_task_source.WillRunTask(); task = registered_task_source.TakeTask(&sequence_transaction); ExpectMockTask(&mock_task_e, &task.value()); @@ -129,7 +130,11 @@ TEST(ThreadPoolSequenceTest, PushTakeRemove) { // Remove the empty slot. The sequence should now be empty. EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction)); - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + + // Sequence is empty and it won't be returned to the priority queue. + EXPECT_FALSE(sequence->has_worker_for_testing()); + EXPECT_FALSE(sequence->is_immediate_for_testing()); + EXPECT_TRUE(sequence->IsEmptyForTesting()); } // Verifies the sort key of a BEST_EFFORT sequence that contains one task. @@ -141,6 +146,7 @@ TEST(ThreadPoolSequenceTest, GetSortKeyBestEffort) { TaskSourceExecutionMode::kParallel); Sequence::Transaction best_effort_sequence_transaction( best_effort_sequence->BeginTransaction()); + best_effort_sequence_transaction.WillPushImmediateTask(); best_effort_sequence_transaction.PushImmediateTask( std::move(best_effort_task)); @@ -176,6 +182,7 @@ TEST(ThreadPoolSequenceTest, GetSortKeyForeground) { TaskSourceExecutionMode::kParallel); Sequence::Transaction foreground_sequence_transaction( foreground_sequence->BeginTransaction()); + foreground_sequence_transaction.WillPushImmediateTask(); foreground_sequence_transaction.PushImmediateTask(std::move(foreground_task)); // Get the sort key. @@ -205,6 +212,7 @@ TEST(ThreadPoolSequenceTest, DidProcessTaskWithoutWillRunTask) { scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>( TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel); Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); + EXPECT_TRUE(sequence_transaction.WillPushImmediateTask()); sequence_transaction.PushImmediateTask( Task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta())); @@ -221,6 +229,7 @@ TEST(ThreadPoolSequenceTest, TakeEmptyFrontSlot) { scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>( TaskTraits(), nullptr, TaskSourceExecutionMode::kParallel); Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); + sequence_transaction.WillPushImmediateTask(); sequence_transaction.PushImmediateTask( Task(FROM_HERE, DoNothing(), TimeTicks::Now(), TimeDelta())); @@ -249,9 +258,9 @@ TEST(ThreadPoolSequenceTest, TakeEmptySequence) { }); } -// Verify that the sequence sets its current location correctly depending on how -// it's interacted with. -TEST(ThreadPoolSequenceTest, PushTakeRemoveTasksWithLocationSetting) { +// Verify that the sequence stays in worker when new tasks are being pushed +// while it's being processed. +TEST(ThreadPoolSequenceTest, SequenceHasWorker) { testing::StrictMock<MockTask> mock_task_a; testing::StrictMock<MockTask> mock_task_b; @@ -259,119 +268,33 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveTasksWithLocationSetting) { MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr, TaskSourceExecutionMode::kParallel); - // sequence location is kNone at creation. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kNone); - Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); - // Push task A in the sequence. ShouldBeQueued() should return + // Push task A in the sequence. WillPushImmediateTask() should return // true since sequence is empty. - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + EXPECT_TRUE(sequence_transaction.WillPushImmediateTask()); sequence_transaction.PushImmediateTask(CreateTask(&mock_task_a)); - // ShouldBeQueued()is called when a new task is about to be - // pushed and sequence will be put in the priority queue or is already in it. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); - - // Push task B into the sequence. ShouldBeQueued() should - // return false. - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); - sequence_transaction.PushImmediateTask(CreateTask(&mock_task_b)); - - // ShouldBeQueued()is called when a new task is about to be - // pushed and sequence will be put in the priority queue or is already in it. - // Sequence location should be kImmediateQueue. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); - auto registered_task_source = RegisteredTaskSource::CreateForTesting(sequence); registered_task_source.WillRunTask(); - // WillRunTask typically indicate that a worker has called GetWork() and - // is ready to run a task so sequence location should have been changed - // to kInWorker. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kInWorker); - - // The next task we get when we call Sequence::TakeTask should be Task A. - absl::optional<Task> task = - registered_task_source.TakeTask(&sequence_transaction); - - // Remove the empty slot. Sequence still has task B. This should return true. - EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); - // Sequence can run immediately. - EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), - &sequence_transaction)); - - // Sequence is not empty so it will be returned to the priority queue and its - // location should be updated to kImmediateQueue. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); - - registered_task_source.WillRunTask(); - - // WillRunTask typically indicate that a worker has called GetWork() and - // is ready to run a task so sequence location should have been changed - // to kInWorker. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kInWorker); - - task = registered_task_source.TakeTask(&sequence_transaction); - - // Remove the empty slot. Sequence is be empty. This should return false. - EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction)); - - // Sequence is empty so it won't be returned to the priority queue and its - // location should be updated to kNone. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kNone); -} - -// Verify that the sequence location stays kInWorker when new tasks are being -// pushed while it's being processed. -TEST(ThreadPoolSequenceTest, CheckSequenceLocationInWorker) { - testing::StrictMock<MockTask> mock_task_a; - testing::StrictMock<MockTask> mock_task_b; - - scoped_refptr<Sequence> sequence = - MakeRefCounted<Sequence>(TaskTraits(TaskPriority::BEST_EFFORT), nullptr, - TaskSourceExecutionMode::kParallel); - - Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); - - // Push task A in the sequence. ShouldBeQueued() should return - // true since sequence is empty. - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); - sequence_transaction.PushImmediateTask(CreateTask(&mock_task_a)); - - auto registered_task_source = - RegisteredTaskSource::CreateForTesting(sequence); - - registered_task_source.WillRunTask(); + // WillRunTask indicates that a worker has called GetWork() and is ready to + // run a task. + EXPECT_TRUE(sequence->has_worker_for_testing()); // The next task we get when we call Sequence::TakeTask should be Task A. absl::optional<Task> task_a = registered_task_source.TakeTask(&sequence_transaction); - // WillRunTask typically indicate that a worker has called GetWork() and - // is ready to run a task so sequence location should have been changed - // to kInWorker. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kInWorker); - - // Push task B into the sequence. ShouldBeQueued() should - // return false. - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + // Push task B into the sequence. WillPushImmediateTask() should return false. + EXPECT_FALSE(sequence_transaction.WillPushImmediateTask()); sequence_transaction.PushImmediateTask(CreateTask(&mock_task_b)); // Sequence is still being processed by a worker so pushing a new task // shouldn't change its location. We should expect it to still be in worker. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kInWorker); + EXPECT_TRUE(sequence->has_worker_for_testing()); // Remove the empty slot. Sequence still has task B. This should return true. EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); @@ -379,10 +302,8 @@ TEST(ThreadPoolSequenceTest, CheckSequenceLocationInWorker) { EXPECT_TRUE(registered_task_source.WillReEnqueue(TimeTicks::Now(), &sequence_transaction)); - // Sequence is not empty so it will be returned to the priority queue and its - // location should be updated to kImmediateQueue. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); + // Sequence is not empty so it will be returned to the priority queue. + EXPECT_FALSE(sequence->has_worker_for_testing()); registered_task_source.WillRunTask(); @@ -393,14 +314,13 @@ TEST(ThreadPoolSequenceTest, CheckSequenceLocationInWorker) { // Remove the empty slot. Sequence is be empty. This should return false. EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction)); - // Sequence is empty so it won't be returned to the priority queue and its - // location should be updated to kNone. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kNone); + // Sequence is empty and it won't be returned to the priority queue. + EXPECT_FALSE(sequence->has_worker_for_testing()); + EXPECT_FALSE(sequence->is_immediate_for_testing()); + EXPECT_TRUE(sequence->IsEmptyForTesting()); } -// Verify that the sequence handle delayed tasks and sets locations -// appropriately +// Verify that the sequence handle delayed tasks. TEST(ThreadPoolSequenceTest, PushTakeRemoveDelayedTasks) { TimeTicks now = TimeTicks::Now(); @@ -417,56 +337,32 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveDelayedTasks) { // Push task A in the sequence. auto delayed_task_a = CreateDelayedTask(&mock_task_a, Milliseconds(20), now); - // TopDelayedTaskWillChange(delayed_task_a) should return - // true since sequence is empty. - EXPECT_TRUE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_a)); - // ShouldBeQueued() should return true since sequence is empty. - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); - // PushImmediateTask(...) should be used for immediate tasks only - // EXPECT_DCHECK_DEATH({ - // sequence_transaction.PushImmediateTask(delayed_task_a); - // }); - sequence_transaction.PushDelayedTask(std::move(delayed_task_a)); - - // Sequence doesn't have immediate tasks so its location should be the delayed - // queue. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kDelayedQueue); + // PushDelayedTask(delayed_task_a, now) should return true since sequence is + // empty. + EXPECT_TRUE(sequence_transaction.PushDelayedTask(std::move(delayed_task_a))); // Push task B into the sequence. auto delayed_task_b = CreateDelayedTask(&mock_task_b, Milliseconds(10), now); - // TopDelayedTaskWillChange(...) should return true since task b runtime is - // earlier than task a's. - EXPECT_TRUE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_b)); - // ShouldBeQueued() should return true since task B is earlier - // than task A. - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); - sequence_transaction.PushDelayedTask(std::move(delayed_task_b)); + // PushDelayedTask(...) should return true since task b runtime is earlier + // than task a's. + EXPECT_TRUE(sequence_transaction.PushDelayedTask(std::move(delayed_task_b))); - // Sequence doesn't have immediate tasks so its location should be the delayed - // queue. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kDelayedQueue); + // Sequence doesn't have immediate tasks. + EXPECT_FALSE(sequence->is_immediate_for_testing()); // Time advances by 15s. now += Milliseconds(15); // Set sequence to ready - sequence->OnBecomeReady(); + EXPECT_TRUE(sequence->OnBecomeReady()); - // Sequence is about to be run so its location should change to immediate - // queue. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); + // Sequence is about to be run. + EXPECT_TRUE(sequence->is_immediate_for_testing()); auto registered_task_source = RegisteredTaskSource::CreateForTesting(sequence); registered_task_source.WillRunTask(); - // WillRunTask() has been called so sequence location should be kInWorker. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kInWorker); - // Take the task in front of the sequence. It should be task B. absl::optional<Task> task = registered_task_source.TakeTask(&sequence_transaction); @@ -477,34 +373,27 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveDelayedTasks) { // so this should return true. EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); - // Task A is still not ready so this should return false and location - // should be set to delayed queue + // Task A is still not ready so this should return false. EXPECT_FALSE( registered_task_source.WillReEnqueue(now, &sequence_transaction)); - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kDelayedQueue); + EXPECT_FALSE(sequence->is_immediate_for_testing()); // Push task C into the sequence. auto delayed_task_c = CreateDelayedTask(&mock_task_c, Milliseconds(1), now); - // TopDelayedTaskWillChange(...) should return true since task c runtime is + // PushDelayedTask(...) should return true since task c runtime is // earlier than task a's. - EXPECT_TRUE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_c)); - // ShouldBeQueued() should return true since task C is earlier - // than task A. - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); - sequence_transaction.PushDelayedTask(std::move(delayed_task_c)); + EXPECT_TRUE(sequence_transaction.PushDelayedTask(std::move(delayed_task_c))); // Push task D into the sequence. auto delayed_task_d = CreateDelayedTask(&mock_task_d, Milliseconds(1), now); - // TopDelayedTaskWillChange(...) should return false since task d queue time + // PushDelayedTask(...) should return false since task d queue time // is later than task c's. - EXPECT_FALSE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_d)); - sequence_transaction.PushDelayedTask(std::move(delayed_task_d)); + EXPECT_FALSE(sequence_transaction.PushDelayedTask(std::move(delayed_task_d))); // Time advances by 2ms. now += Milliseconds(2); // Set sequence to ready - registered_task_source.OnBecomeReady(); + EXPECT_TRUE(registered_task_source->OnBecomeReady()); registered_task_source.WillRunTask(); @@ -516,11 +405,9 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveDelayedTasks) { // Remove the empty slot. Task D should now be in front. EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); - // Task D is ready so this should return true and location - // should be set to immediate queue + // Task D is ready so this should return true. EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction)); - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); + EXPECT_TRUE(sequence->is_immediate_for_testing()); registered_task_source.WillRunTask(); @@ -535,11 +422,9 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveDelayedTasks) { // Time advances by 10ms. now += Milliseconds(10); - // Task A is ready so this should return true and location - // should be set to immediate queue + // Task A is ready so this should return true. EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction)); - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); + EXPECT_TRUE(sequence->is_immediate_for_testing()); registered_task_source.WillRunTask(); @@ -550,16 +435,14 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveDelayedTasks) { // Remove the empty slot. Sequence should be empty now. EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction)); - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kNone); - // Sequence is empty so there should be no task to execute. - // This should return true - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + // Sequence is empty and it won't be returned to the priority queue. + EXPECT_FALSE(sequence->has_worker_for_testing()); + EXPECT_FALSE(sequence->is_immediate_for_testing()); + EXPECT_TRUE(sequence->IsEmptyForTesting()); } -// Verify that the sequence handle delayed and immediate tasks and sets -// locations appropriately +// Verify that the sequence handle delayed and immediate tasks. TEST(ThreadPoolSequenceTest, PushTakeRemoveMixedTasks) { TimeTicks now = TimeTicks::Now(); @@ -577,39 +460,29 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveMixedTasks) { // Starting with a delayed task // Push task A in the sequence. auto delayed_task_a = CreateDelayedTask(&mock_task_a, Milliseconds(20), now); - // TopDelayedTaskWillChange(delayed_task_a) should return + // PushDelayedTask(delayed_task_a) should return // true since sequence is empty. - EXPECT_TRUE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_a)); - // ShouldBeQueued() should return true since sequence is empty. - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); - sequence_transaction.PushDelayedTask(std::move(delayed_task_a)); - - // Sequence doesn't have immediate tasks so its location should be the delayed - // queue. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kDelayedQueue); + EXPECT_TRUE(sequence_transaction.PushDelayedTask(std::move(delayed_task_a))); + // Sequence doesn't have immediate tasks. + EXPECT_FALSE(sequence->is_immediate_for_testing()); // Push an immediate task while a delayed task is already sitting in the // delayed queue. This should prompt a move to the immediate queue. // Push task B in the sequence. auto task_b = CreateTask(&mock_task_b, now); - // ShouldBeQueued() should return true since sequence is in delayed queue. - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); + // WillPushImmediateTask() should return true since sequence is in delayed + // queue. + EXPECT_TRUE(sequence_transaction.WillPushImmediateTask()); sequence_transaction.PushImmediateTask(std::move(task_b)); - // Sequence doesn't have immediate tasks so its location should will change - // to immediate queue. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); + // Sequence now has an immediate tasks. + EXPECT_TRUE(sequence->is_immediate_for_testing()); auto registered_task_source = RegisteredTaskSource::CreateForTesting(sequence); // Prepare to run a task. registered_task_source.WillRunTask(); - - // WillRunTask() has been called so sequence location should be kInWorker. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kInWorker); + EXPECT_TRUE(sequence->has_worker_for_testing()); // Take the task in front of the sequence. It should be task B. absl::optional<Task> task = @@ -624,31 +497,21 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveMixedTasks) { // Time advances by 21ms. now += Milliseconds(21); - // Task A is ready so this should return true and location - // should be set to immediate queue. + // Task A is ready so this should return true. EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction)); - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); + EXPECT_TRUE(sequence->is_immediate_for_testing()); registered_task_source.WillRunTask(); + EXPECT_TRUE(sequence->has_worker_for_testing()); - // WillRunTask() has been called so sequence location should be kInWorker. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kInWorker); - - // Push a delayed task while sequence is being run by a worker. - // Push task C in the sequence. + // Push a delayed task while sequence is being run by a worker. Push task C in + // the sequence. auto delayed_task_c = CreateDelayedTask(&mock_task_c, Milliseconds(5), now); - // TopDelayedTaskWillChange(delayed_task_c) should return - // false since task A is ripe and earlier than task C. - EXPECT_FALSE(sequence_transaction.TopDelayedTaskWillChange(delayed_task_c)); - // ShouldBeQueued() should return false since sequence is in worker. - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); - sequence_transaction.PushDelayedTask(std::move(delayed_task_c)); - - // Sequence is in worker. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kInWorker); + // PushDelayedTask(delayed_task_c) should return false since sequence is in + // worker. + EXPECT_FALSE(sequence_transaction.PushDelayedTask(std::move(delayed_task_c))); + // Sequence is still in worker. + EXPECT_TRUE(sequence->has_worker_for_testing()); // This should return task A task = registered_task_source.TakeTask(&sequence_transaction); @@ -661,37 +524,28 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveMixedTasks) { // Time advances by 2ms. now += Milliseconds(2); - // Task C is not ready so this should return false and location should be set - // to delayed queue. + // Task C is not ready so this should return false. EXPECT_FALSE( registered_task_source.WillReEnqueue(now, &sequence_transaction)); - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kDelayedQueue); + EXPECT_FALSE(sequence->is_immediate_for_testing()); // Time advances by 4ms. Task C becomes ready. now += Milliseconds(4); // Set sequence to ready - registered_task_source.OnBecomeReady(); - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); + EXPECT_TRUE(registered_task_source->OnBecomeReady()); + EXPECT_TRUE(sequence->is_immediate_for_testing()); // Push task D in the sequence while sequence is ready. auto task_d = CreateTask(&mock_task_d, now); - // ShouldBeQueued() should return false since sequence is already in immediate - // queue. - EXPECT_FALSE(sequence_transaction.ShouldBeQueued()); + // WillPushImmediateTask() should return false since sequence is already in + // immediate queue. + EXPECT_FALSE(sequence_transaction.WillPushImmediateTask()); sequence_transaction.PushImmediateTask(std::move(task_d)); - - // Sequence should be in immediate queue. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); + EXPECT_TRUE(sequence->is_immediate_for_testing()); registered_task_source.WillRunTask(); - - // WillRunTask() has been called so sequence location should be kInWorker. - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kInWorker); + EXPECT_TRUE(sequence->has_worker_for_testing()); // This should return task C since was ready before Task D was posted. task = registered_task_source.TakeTask(&sequence_transaction); @@ -701,11 +555,9 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveMixedTasks) { // Remove the empty slot. Task D should now be in front. EXPECT_TRUE(registered_task_source.DidProcessTask(&sequence_transaction)); - // Task D should be run so this should return true and location should be set - // to immediate queue. + // Task D should be run so this should return true. EXPECT_TRUE(registered_task_source.WillReEnqueue(now, &sequence_transaction)); - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kImmediateQueue); + EXPECT_TRUE(sequence->is_immediate_for_testing()); registered_task_source.WillRunTask(); @@ -716,8 +568,8 @@ TEST(ThreadPoolSequenceTest, PushTakeRemoveMixedTasks) { // Remove the empty slot. Sequence should be empty. EXPECT_FALSE(registered_task_source.DidProcessTask(&sequence_transaction)); - EXPECT_EQ(sequence->GetCurrentLocationForTesting(), - Sequence::SequenceLocation::kNone); + EXPECT_FALSE(sequence->has_worker_for_testing()); + EXPECT_FALSE(sequence->is_immediate_for_testing()); } // Test that PushDelayedTask method is used only for delayed tasks @@ -732,9 +584,6 @@ TEST(ThreadPoolSequenceTest, TestPushDelayedTaskMethodUsage) { // Push task B in the sequence. auto task_a = CreateTask(&mock_task_a); - // ShouldBeQueued() should return true since sequence is empty. - EXPECT_TRUE(sequence_transaction.ShouldBeQueued()); - // PushDelayedTask(...) should be used for delayed tasks only. EXPECT_DCHECK_DEATH( { sequence_transaction.PushDelayedTask(std::move(task_a)); }); } @@ -753,8 +602,8 @@ TEST(ThreadPoolSequenceTest, GetDelayedSortKeyMixedtasks) { Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); // Create a first delayed task. - sequence_transaction.PushDelayedTask( - CreateDelayedTask(&mock_task_a, Milliseconds(10), now)); + EXPECT_TRUE(sequence_transaction.PushDelayedTask( + CreateDelayedTask(&mock_task_a, Milliseconds(10), now))); // Get the delayed sort key (first time). const TimeTicks sort_key_1 = sequence->GetDelayedSortKey(); @@ -764,6 +613,7 @@ TEST(ThreadPoolSequenceTest, GetDelayedSortKeyMixedtasks) { // Push an immediate task that should run after the delayed task. auto immediate_task = CreateTask(&mock_task_b, now); + sequence_transaction.WillPushImmediateTask(); sequence_transaction.PushImmediateTask(std::move(immediate_task)); // Get the delayed sort key (second time). @@ -836,9 +686,9 @@ TEST(ThreadPoolSequenceTest, GetDelayedSortKeyDelayedtasks) { // Time advances by 11ms now += Milliseconds(11); + sequence->OnBecomeReady(); auto registered_task_source = RegisteredTaskSource::CreateForTesting(sequence); - registered_task_source.OnBecomeReady(); registered_task_source.WillRunTask(); absl::optional<Task> task = registered_task_source.TakeTask(&sequence_transaction); diff --git a/chromium/base/task/thread_pool/task_source.cc b/chromium/base/task/thread_pool/task_source.cc index 2c254305b53..c8966f435d0 100644 --- a/chromium/base/task/thread_pool/task_source.cc +++ b/chromium/base/task/thread_pool/task_source.cc @@ -123,13 +123,6 @@ RegisteredTaskSource& RegisteredTaskSource::operator=( return *this; } -void RegisteredTaskSource::OnBecomeReady() { -#if DCHECK_IS_ON() - DCHECK_EQ(run_step_, State::kInitial); -#endif // DCHECK_IS_ON() - task_source_->OnBecomeReady(); -} - TaskSource::RunStatus RegisteredTaskSource::WillRunTask() { TaskSource::RunStatus run_status = task_source_->WillRunTask(); #if DCHECK_IS_ON() diff --git a/chromium/base/task/thread_pool/task_source.h b/chromium/base/task/thread_pool/task_source.h index 4f37db6ce37..f9c6dff2148 100644 --- a/chromium/base/task/thread_pool/task_source.h +++ b/chromium/base/task/thread_pool/task_source.h @@ -39,17 +39,22 @@ struct BASE_EXPORT ExecutionEnvironment { }; // A TaskSource is a virtual class that provides a series of Tasks that must be -// executed. +// executed immediately or in the future. // -// A task source is registered when it's ready to be queued. A task source is -// ready to be queued when either: +// When a task source has delayed tasks but no immediate tasks, the scheduler +// must call OnBecomeReady() after HasReadyTasks(now) == true, which is +// guaranteed once now >= GetDelayedSortKey(). +// +// A task source is registered when it's ready to be added to the immediate +// queue. A task source is ready to be queued when either: // 1- It has new tasks that can run concurrently as a result of external -// operations, e.g. posting a new task to an empty Sequence or increasing -// max concurrency of a JobTaskSource; +// operations, e.g. posting a new immediate task to an empty Sequence or +// increasing max concurrency of a JobTaskSource; // 2- A worker finished running a task from it and both DidProcessTask() and // WillReEnqueue() returned true; or // 3- A worker is about to run a task from it and WillRunTask() returned // kAllowedNotSaturated. +// 4- A delayed task became ready and OnBecomeReady() returns true. // // A worker may perform the following sequence of operations on a // RegisteredTaskSource after obtaining it from the queue: @@ -152,6 +157,13 @@ class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> { // Returns a Timeticks object representing the next delayed runtime of the // TaskSource. virtual TimeTicks GetDelayedSortKey() const = 0; + // Returns true if there are tasks ready to be executed. Thread-safe but the + // returned value may immediately be obsolete. + virtual bool HasReadyTasks(TimeTicks now) const = 0; + // Returns true if the TaskSource should be moved to the immediate queue + // due to ready delayed tasks. Note: Returns false if the TaskSource contains + // ready delayed tasks, but expects to already be in the immediate queue. + virtual bool OnBecomeReady() = 0; // Support for IntrusiveHeap in ThreadGroup::PriorityQueue. void SetImmediateHeapHandle(const HeapHandle& handle); @@ -224,7 +236,6 @@ class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> { mutable CheckedLock lock_{UniversalPredecessor()}; private: - virtual void OnBecomeReady() = 0; friend class RefCountedThreadSafe<TaskSource>; friend class RegisteredTaskSource; @@ -284,10 +295,6 @@ class BASE_EXPORT RegisteredTaskSource { // that indicates if the operation is allowed (TakeTask() can be called). TaskSource::RunStatus WillRunTask(); - // Informs this TaskSource that it has become ready to run and is being moved - // from delayed to immediate queue. - void OnBecomeReady(); - // Returns the next task to run from this TaskSource. This should be called // only after WillRunTask() returned RunStatus::kAllowed*. |transaction| is // optional and should only be provided if this operation is already part of diff --git a/chromium/base/task/thread_pool/task_tracker.cc b/chromium/base/task/thread_pool/task_tracker.cc index b81bf4633c8..98f316c71d3 100644 --- a/chromium/base/task/thread_pool/task_tracker.cc +++ b/chromium/base/task/thread_pool/task_tracker.cc @@ -23,11 +23,11 @@ #include "base/synchronization/condition_variable.h" #include "base/synchronization/waitable_event.h" #include "base/task/scoped_set_task_priority_for_current_thread.h" +#include "base/task/sequenced_task_runner.h" +#include "base/task/single_thread_task_runner.h" #include "base/task/task_executor.h" #include "base/threading/sequence_local_storage_map.h" -#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/thread_restrictions.h" -#include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "base/trace_event/base_tracing.h" #include "base/values.h" @@ -319,7 +319,8 @@ bool TaskTracker::WillPostTask(Task* task, // ordering bug. This aims to catch those early. CheckedAutoLock auto_lock(shutdown_lock_); DCHECK(shutdown_event_); - DCHECK(!shutdown_event_->IsSignaled()); + DCHECK(!shutdown_event_->IsSignaled()) + << "posted_from: " << task->posted_from.ToString(); } // TODO(scheduler-dev): Record the task traits here. @@ -328,7 +329,8 @@ bool TaskTracker::WillPostTask(Task* task, return true; } -bool TaskTracker::WillPostTaskNow(const Task& task, TaskPriority priority) { +bool TaskTracker::WillPostTaskNow(const Task& task, + TaskPriority priority) const { // Delayed tasks's TaskShutdownBehavior is implicitly capped at // SKIP_ON_SHUTDOWN. i.e. it cannot BLOCK_SHUTDOWN, TaskTracker will not wait // for a delayed task in a BLOCK_SHUTDOWN TaskSource and will also skip @@ -387,6 +389,10 @@ RegisteredTaskSource TaskTracker::RunAndPopNextTask( } if (task) { + // Skip delayed tasks if shutdown started. + if (!task->delayed_run_time.is_null() && state_->HasShutdownStarted()) + task->task = base::DoNothingWithBoundArgs(std::move(task->task)); + // Run the |task| (whether it's a worker task or the Clear() closure). RunTask(std::move(task.value()), task_source.get(), traits); } @@ -443,9 +449,12 @@ void TaskTracker::RunTask(Task task, ? environment.sequence_local_storage.get() : &local_storage_map.value()); - // Set up TaskRunnerHandle as expected for the scope of the task. - absl::optional<SequencedTaskRunnerHandle> sequenced_task_runner_handle; - absl::optional<ThreadTaskRunnerHandle> single_thread_task_runner_handle; + // Set up TaskRunner CurrentDefaultHandle as expected for the scope of the + // task. + absl::optional<SequencedTaskRunner::CurrentDefaultHandle> + sequenced_task_runner_handle; + absl::optional<SingleThreadTaskRunner::CurrentDefaultHandle> + single_thread_task_runner_handle; switch (task_source->execution_mode()) { case TaskSourceExecutionMode::kJob: case TaskSourceExecutionMode::kParallel: diff --git a/chromium/base/task/thread_pool/task_tracker.h b/chromium/base/task/thread_pool/task_tracker.h index 6b53bcca6b7..ce86c56c522 100644 --- a/chromium/base/task/thread_pool/task_tracker.h +++ b/chromium/base/task/thread_pool/task_tracker.h @@ -100,7 +100,8 @@ class BASE_EXPORT TaskTracker { // Informs this TaskTracker that |task| that is about to be pushed to a task // source with |priority|. Returns true if this operation is allowed (the // operation should be performed if-and-only-if it is). - [[nodiscard]] bool WillPostTaskNow(const Task& task, TaskPriority priority); + [[nodiscard]] bool WillPostTaskNow(const Task& task, + TaskPriority priority) const; // Informs this TaskTracker that |task_source| is about to be queued. Returns // a RegisteredTaskSource that should be queued if-and-only-if it evaluates to @@ -216,9 +217,6 @@ class BASE_EXPORT TaskTracker { TaskAnnotator task_annotator_; - // Suffix for histograms recorded by this TaskTracker. - const std::string histogram_label_; - // Indicates whether logging information about TaskPriority::BEST_EFFORT tasks // was enabled with a command line switch. const bool has_log_best_effort_tasks_switch_; diff --git a/chromium/base/task/thread_pool/task_tracker_unittest.cc b/chromium/base/task/thread_pool/task_tracker_unittest.cc index c85a8a0e9eb..55b89640228 100644 --- a/chromium/base/task/thread_pool/task_tracker_unittest.cc +++ b/chromium/base/task/thread_pool/task_tracker_unittest.cc @@ -36,10 +36,8 @@ #include "base/test/test_waitable_event.h" #include "base/threading/platform_thread.h" #include "base/threading/scoped_blocking_call.h" -#include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/simple_thread.h" #include "base/threading/thread_restrictions.h" -#include "base/threading/thread_task_runner_handle.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -122,7 +120,11 @@ class ThreadPostingAndRunningTask : public SimpleThread { post_and_queue_succeeded = tracker_->WillPostTask(&task_, sequence_->shutdown_behavior()); - sequence_->BeginTransaction().PushImmediateTask(std::move(task_)); + { + auto transaction = sequence_->BeginTransaction(); + transaction.WillPushImmediateTask(); + transaction.PushImmediateTask(std::move(task_)); + } task_source_ = tracker_->RegisterTaskSource(std::move(sequence_)); post_and_queue_succeeded &= !!task_source_; @@ -536,8 +538,8 @@ static void RunTaskRunnerHandleVerificationTask( // Confirm that the test conditions are right (no TaskRunnerHandles set // already). - EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); - EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault()); + EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault()); test::QueueAndRunTaskSource( tracker, @@ -545,13 +547,13 @@ static void RunTaskRunnerHandleVerificationTask( std::move(task_runner), execution_mode)); // TaskRunnerHandle state is reset outside of task's scope. - EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); - EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault()); + EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault()); } static void VerifyNoTaskRunnerHandle() { - EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); - EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault()); + EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault()); } TEST_P(ThreadPoolTaskTrackerTest, TaskRunnerHandleIsNotSetOnParallel) { @@ -567,9 +569,9 @@ TEST_P(ThreadPoolTaskTrackerTest, TaskRunnerHandleIsNotSetOnParallel) { static void VerifySequencedTaskRunnerHandle( const SequencedTaskRunner* expected_task_runner) { - EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); - EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet()); - EXPECT_EQ(expected_task_runner, SequencedTaskRunnerHandle::Get()); + EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault()); + EXPECT_TRUE(SequencedTaskRunner::HasCurrentDefault()); + EXPECT_EQ(expected_task_runner, SequencedTaskRunner::GetCurrentDefault()); } TEST_P(ThreadPoolTaskTrackerTest, SequencedTaskRunnerHandleIsSetOnSequenced) { @@ -590,10 +592,10 @@ TEST_P(ThreadPoolTaskTrackerTest, SequencedTaskRunnerHandleIsSetOnSequenced) { static void VerifyThreadTaskRunnerHandle( const SingleThreadTaskRunner* expected_task_runner) { - EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet()); + EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault()); // SequencedTaskRunnerHandle inherits ThreadTaskRunnerHandle for thread. - EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet()); - EXPECT_EQ(expected_task_runner, ThreadTaskRunnerHandle::Get()); + EXPECT_TRUE(SequencedTaskRunner::HasCurrentDefault()); + EXPECT_EQ(expected_task_runner, SingleThreadTaskRunner::GetCurrentDefault()); } TEST_P(ThreadPoolTaskTrackerTest, ThreadTaskRunnerHandleIsSetOnSingleThreaded) { @@ -958,6 +960,7 @@ TEST_F(ThreadPoolTaskTrackerTest, CurrentSequenceToken) { { Sequence::Transaction sequence_transaction(sequence->BeginTransaction()); + sequence_transaction.WillPushImmediateTask(); sequence_transaction.PushImmediateTask(std::move(task)); EXPECT_FALSE(SequenceToken::GetForCurrentThread().IsValid()); @@ -1158,7 +1161,11 @@ TEST_F(ThreadPoolTaskTrackerTest, scoped_refptr<Sequence> sequence = test::CreateSequenceWithTask(std::move(task_1), default_traits); - sequence->BeginTransaction().PushImmediateTask(std::move(task_2)); + { + auto transaction = sequence->BeginTransaction(); + transaction.WillPushImmediateTask(); + transaction.PushImmediateTask(std::move(task_2)); + } EXPECT_EQ(sequence, test::QueueAndRunTaskSource(&tracker_, sequence).Unregister()); } diff --git a/chromium/base/task/thread_pool/test_task_factory.cc b/chromium/base/task/thread_pool/test_task_factory.cc index 72186140dd8..b1c17f9f986 100644 --- a/chromium/base/task/thread_pool/test_task_factory.cc +++ b/chromium/base/task/thread_pool/test_task_factory.cc @@ -10,8 +10,8 @@ #include "base/check_op.h" #include "base/location.h" #include "base/synchronization/waitable_event.h" -#include "base/threading/sequenced_task_runner_handle.h" -#include "base/threading/thread_task_runner_handle.h" +#include "base/task/sequenced_task_runner.h" +#include "base/task/single_thread_task_runner.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -63,21 +63,21 @@ void TestTaskFactory::RunTaskCallback(size_t task_index, switch (execution_mode_) { case TaskSourceExecutionMode::kJob: case TaskSourceExecutionMode::kParallel: - EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); - EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet()); + EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault()); + EXPECT_FALSE(SequencedTaskRunner::HasCurrentDefault()); break; case TaskSourceExecutionMode::kSequenced: - EXPECT_FALSE(ThreadTaskRunnerHandle::IsSet()); - EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet()); - EXPECT_EQ(task_runner_, SequencedTaskRunnerHandle::Get()); + EXPECT_FALSE(SingleThreadTaskRunner::HasCurrentDefault()); + EXPECT_TRUE(SequencedTaskRunner::HasCurrentDefault()); + EXPECT_EQ(task_runner_, SequencedTaskRunner::GetCurrentDefault()); break; case TaskSourceExecutionMode::kSingleThread: // SequencedTaskRunnerHandle inherits from ThreadTaskRunnerHandle so // both are expected to be "set" in the kSingleThread case. - EXPECT_TRUE(ThreadTaskRunnerHandle::IsSet()); - EXPECT_TRUE(SequencedTaskRunnerHandle::IsSet()); - EXPECT_EQ(task_runner_, ThreadTaskRunnerHandle::Get()); - EXPECT_EQ(task_runner_, SequencedTaskRunnerHandle::Get()); + EXPECT_TRUE(SingleThreadTaskRunner::HasCurrentDefault()); + EXPECT_TRUE(SequencedTaskRunner::HasCurrentDefault()); + EXPECT_EQ(task_runner_, SingleThreadTaskRunner::GetCurrentDefault()); + EXPECT_EQ(task_runner_, SequencedTaskRunner::GetCurrentDefault()); break; } diff --git a/chromium/base/task/thread_pool/test_utils.cc b/chromium/base/task/thread_pool/test_utils.cc index 6144f617a6d..46e4cbd72d2 100644 --- a/chromium/base/task/thread_pool/test_utils.cc +++ b/chromium/base/task/thread_pool/test_utils.cc @@ -107,7 +107,9 @@ scoped_refptr<Sequence> CreateSequenceWithTask( TaskSourceExecutionMode execution_mode) { scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(traits, task_runner.get(), execution_mode); - sequence->BeginTransaction().PushImmediateTask(std::move(task)); + auto transaction = sequence->BeginTransaction(); + transaction.WillPushImmediateTask(); + transaction.PushImmediateTask(std::move(task)); return sequence; } @@ -190,7 +192,7 @@ void MockPooledTaskRunnerDelegate::PostTaskWithSequenceNow( Task task, scoped_refptr<Sequence> sequence) { auto transaction = sequence->BeginTransaction(); - const bool sequence_should_be_queued = transaction.ShouldBeQueued(); + const bool sequence_should_be_queued = transaction.WillPushImmediateTask(); RegisteredTaskSource task_source; if (sequence_should_be_queued) { task_source = task_tracker_->RegisterTaskSource(std::move(sequence)); diff --git a/chromium/base/task/thread_pool/test_utils.h b/chromium/base/task/thread_pool/test_utils.h index 47c2534cdc4..e7a43dbcd96 100644 --- a/chromium/base/task/thread_pool/test_utils.h +++ b/chromium/base/task/thread_pool/test_utils.h @@ -118,15 +118,6 @@ class MockJobTask : public base::RefCountedThreadSafe<MockJobTask> { std::atomic_size_t remaining_num_tasks_to_run_; }; -// An enumeration of possible thread pool types. Used to parametrize relevant -// thread_pool tests. -enum class GroupType { - GENERIC, -#if HAS_NATIVE_THREAD_POOL() - NATIVE, -#endif -}; - // Creates a Sequence with given |traits| and pushes |task| to it. If a // TaskRunner is associated with |task|, it should be be passed as |task_runner| // along with its |execution_mode|. Returns the created Sequence. diff --git a/chromium/base/task/thread_pool/thread_group.cc b/chromium/base/task/thread_pool/thread_group.cc index 0fcdc194d8b..56d8747540f 100644 --- a/chromium/base/task/thread_pool/thread_group.cc +++ b/chromium/base/task/thread_pool/thread_group.cc @@ -266,6 +266,27 @@ void ThreadGroup::InvalidateAndHandoffAllTaskSourcesToOtherThreadGroup( replacement_thread_group_ = destination_thread_group; } +void ThreadGroup::HandoffNonUserBlockingTaskSourcesToOtherThreadGroup( + ThreadGroup* destination_thread_group) { + CheckedAutoLock current_thread_group_lock(lock_); + CheckedAutoLock destination_thread_group_lock( + destination_thread_group->lock_); + PriorityQueue new_priority_queue; + TaskSourceSortKey top_sort_key; + // This works because all USER_BLOCKING tasks are at the front of the queue. + while (!priority_queue_.IsEmpty() && + (top_sort_key = priority_queue_.PeekSortKey()).priority() == + TaskPriority::USER_BLOCKING) { + new_priority_queue.Push(priority_queue_.PopTaskSource(), top_sort_key); + } + while (!priority_queue_.IsEmpty()) { + top_sort_key = priority_queue_.PeekSortKey(); + destination_thread_group->priority_queue_.Push( + priority_queue_.PopTaskSource(), top_sort_key); + } + priority_queue_ = std::move(new_priority_queue); +} + bool ThreadGroup::ShouldYield(TaskSourceSortKey sort_key) { DCHECK(TS_UNCHECKED_READ(max_allowed_sort_key_).is_lock_free()); @@ -325,5 +346,10 @@ ThreadGroup::GetScopedWindowsThreadEnvironment(WorkerEnvironment environment) { } #endif +// static +bool ThreadGroup::CurrentThreadHasGroup() { + return GetCurrentThreadGroup() != nullptr; +} + } // namespace internal } // namespace base diff --git a/chromium/base/task/thread_pool/thread_group.h b/chromium/base/task/thread_pool/thread_group.h index 505433ca3bd..f2c8da5e754 100644 --- a/chromium/base/task/thread_pool/thread_group.h +++ b/chromium/base/task/thread_pool/thread_group.h @@ -96,6 +96,11 @@ class BASE_EXPORT ThreadGroup { void InvalidateAndHandoffAllTaskSourcesToOtherThreadGroup( ThreadGroup* destination_thread_group); + // Move all task sources except the ones with TaskPriority::USER_BLOCKING, + // from this ThreadGroup's PriorityQueue to the |destination_thread_group|'s. + void HandoffNonUserBlockingTaskSourcesToOtherThreadGroup( + ThreadGroup* destination_thread_group); + // Returns true if a task with |sort_key| running in this thread group should // return ASAP, either because its priority is not allowed to run or because // work of higher priority is pending. Thread-safe but may return an outdated @@ -123,6 +128,10 @@ class BASE_EXPORT ThreadGroup { virtual void OnShutdownStarted() = 0; + // Returns true if a thread group is registered in TLS. Used by diagnostic + // code to check whether it's inside a ThreadPool task. + static bool CurrentThreadHasGroup(); + protected: // Derived classes must implement a ScopedCommandsExecutor that derives from // this to perform operations at the end of a scope, when all locks have been diff --git a/chromium/base/task/thread_pool/thread_group_impl.cc b/chromium/base/task/thread_pool/thread_group_impl.cc index b72cebb9aa8..32036d47f1f 100644 --- a/chromium/base/task/thread_pool/thread_group_impl.cc +++ b/chromium/base/task/thread_pool/thread_group_impl.cc @@ -20,6 +20,7 @@ #include "base/location.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" +#include "base/metrics/histogram_macros.h" #include "base/numerics/clamped_math.h" #include "base/ranges/algorithm.h" #include "base/sequence_token.h" @@ -195,8 +196,10 @@ class ThreadGroupImpl::ScopedCommandsExecutor class ThreadGroupImpl::WorkerThreadDelegateImpl : public WorkerThread::Delegate, public BlockingObserver { public: - // |outer| owns the worker for which this delegate is constructed. - explicit WorkerThreadDelegateImpl(TrackedRef<ThreadGroupImpl> outer); + // |outer| owns the worker for which this delegate is constructed. If + // |is_excess| is true, this worker will be eligible for reclaim. + explicit WorkerThreadDelegateImpl(TrackedRef<ThreadGroupImpl> outer, + bool is_excess); WorkerThreadDelegateImpl(const WorkerThreadDelegateImpl&) = delete; WorkerThreadDelegateImpl& operator=(const WorkerThreadDelegateImpl&) = delete; @@ -211,6 +214,7 @@ class ThreadGroupImpl::WorkerThreadDelegateImpl : public WorkerThread::Delegate, void DidProcessTask(RegisteredTaskSource task_source) override; TimeDelta GetSleepTimeout() override; void OnMainExit(WorkerThread* worker) override; + void RecordUnnecessaryWakeup() override; // BlockingObserver: void BlockingStarted(BlockingType blocking_type) override; @@ -223,7 +227,7 @@ class ThreadGroupImpl::WorkerThreadDelegateImpl : public WorkerThread::Delegate, EXCLUSIVE_LOCKS_REQUIRED(outer_->lock_); // Returns true iff the worker can get work. Cleans up the worker or puts it - // on the idle stack if it can't get work. + // on the idle set if it can't get work. bool CanGetWorkLockRequired(ScopedCommandsExecutor* executor, WorkerThread* worker) EXCLUSIVE_LOCKS_REQUIRED(outer_->lock_); @@ -241,6 +245,9 @@ class ThreadGroupImpl::WorkerThreadDelegateImpl : public WorkerThread::Delegate, return *read_any().current_task_priority; } + // True if this worker is be eligible for reclaim. + bool is_excess() const { return is_excess_; } + // Exposed for AnnotateAcquiredLockAlias const CheckedLock& lock() const LOCK_RETURNED(outer_->lock_) { return outer_->lock_; @@ -314,6 +321,8 @@ class ThreadGroupImpl::WorkerThreadDelegateImpl : public WorkerThread::Delegate, const TrackedRef<ThreadGroupImpl> outer_; + const bool is_excess_; + // Whether |outer_->max_tasks_|/|outer_->max_best_effort_tasks_| were // incremented due to a ScopedBlockingCall on the thread. bool incremented_max_tasks_since_blocked_ GUARDED_BY(outer_->lock_) = false; @@ -332,11 +341,15 @@ ThreadGroupImpl::ThreadGroupImpl(StringPiece histogram_label, StringPiece thread_group_label, ThreadType thread_type_hint, TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate) - : ThreadGroup(std::move(task_tracker), std::move(delegate)), + TrackedRef<Delegate> delegate, + ThreadGroup* predecessor_thread_group) + : ThreadGroup(std::move(task_tracker), + std::move(delegate), + predecessor_thread_group), + histogram_label_(histogram_label), thread_group_label_(thread_group_label), thread_type_hint_(thread_type_hint), - idle_workers_stack_cv_for_testing_(lock_.CreateConditionVariable()), + idle_workers_set_cv_for_testing_(lock_.CreateConditionVariable()), tracked_ref_factory_(this) { DCHECK(!thread_group_label_.empty()); } @@ -357,12 +370,13 @@ void ThreadGroupImpl::Start( in_start().no_worker_reclaim = FeatureList::IsEnabled(kNoWorkerThreadReclaim); in_start().may_block_threshold = may_block_threshold ? may_block_threshold.value() - : (thread_type_hint_ == ThreadType::kDefault + : (thread_type_hint_ != ThreadType::kBackground ? kForegroundMayBlockThreshold : kBackgroundMayBlockThreshold); in_start().blocked_workers_poll_period = - thread_type_hint_ == ThreadType::kDefault ? kForegroundBlockedWorkersPoll - : kBackgroundBlockedWorkersPoll; + thread_type_hint_ != ThreadType::kBackground + ? kForegroundBlockedWorkersPoll + : kBackgroundBlockedWorkersPoll; ScopedCommandsExecutor executor(this); CheckedAutoLock auto_lock(lock_); @@ -499,12 +513,13 @@ size_t ThreadGroupImpl::GetMaxBestEffortTasksForTesting() const { size_t ThreadGroupImpl::NumberOfIdleWorkersForTesting() const { CheckedAutoLock auto_lock(lock_); - return idle_workers_stack_.Size(); + return idle_workers_set_.Size(); } ThreadGroupImpl::WorkerThreadDelegateImpl::WorkerThreadDelegateImpl( - TrackedRef<ThreadGroupImpl> outer) - : outer_(std::move(outer)) { + TrackedRef<ThreadGroupImpl> outer, + bool is_excess) + : outer_(std::move(outer)), is_excess_(is_excess) { // Bound in OnMainEntry(). DETACH_FROM_THREAD(worker_thread_checker_); } @@ -592,7 +607,7 @@ RegisteredTaskSource ThreadGroupImpl::WorkerThreadDelegateImpl::GetWork( // Running task bookkeeping. outer_->IncrementTasksRunningLockRequired(priority); - DCHECK(!outer_->idle_workers_stack_.Contains(worker)); + DCHECK(!outer_->idle_workers_set_.Contains(worker)); write_worker().current_task_priority = priority; write_worker().current_shutdown_behavior = task_source->shutdown_behavior(); @@ -651,7 +666,7 @@ void ThreadGroupImpl::WorkerThreadDelegateImpl::DidProcessTask( TimeDelta ThreadGroupImpl::WorkerThreadDelegateImpl::GetSleepTimeout() { DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_); - if (outer_->after_start().no_worker_reclaim) + if (!is_excess()) return TimeDelta::Max(); // Sleep for an extra 10% to avoid the following pathological case: // 0) A task is running on a timer which matches @@ -659,13 +674,13 @@ TimeDelta ThreadGroupImpl::WorkerThreadDelegateImpl::GetSleepTimeout() { // 1) The timer fires and this worker is created by // MaintainAtLeastOneIdleWorkerLockRequired() because the last idle // worker was assigned the task. - // 2) This worker begins sleeping |after_start().suggested_reclaim_time| (on - // top of the idle stack). + // 2) This worker begins sleeping |after_start().suggested_reclaim_time| (at + // the front of the idle set). // 3) The task assigned to the other worker completes and the worker goes - // back on the idle stack (this worker is now second on the idle stack; + // back in the idle set (this worker may now second on the idle set; // its GetLastUsedTime() is set to Now()). // 4) The sleep in (2) expires. Since (3) was fast this worker is likely to - // have been second on the idle stack long enough for + // have been second on the idle set long enough for // CanCleanupLockRequired() to be satisfied in which case this worker is // cleaned up. // 5) The timer fires at roughly the same time and we're back to (1) if (4) @@ -687,7 +702,7 @@ TimeDelta ThreadGroupImpl::WorkerThreadDelegateImpl::GetSleepTimeout() { bool ThreadGroupImpl::WorkerThreadDelegateImpl::CanCleanupLockRequired( const WorkerThread* worker) const { DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_); - if (outer_->after_start().no_worker_reclaim) + if (!is_excess()) return false; const TimeTicks last_used_time = worker->GetLastUsedTime(); @@ -705,8 +720,8 @@ void ThreadGroupImpl::WorkerThreadDelegateImpl::CleanupLockRequired( worker->Cleanup(); - if (outer_->IsOnIdleStackLockRequired(worker)) - outer_->idle_workers_stack_.Remove(worker); + if (outer_->IsOnIdleSetLockRequired(worker)) + outer_->idle_workers_set_.Remove(worker); // Remove the worker from |workers_|. auto worker_iter = ranges::find(outer_->workers_, worker); @@ -718,23 +733,12 @@ void ThreadGroupImpl::WorkerThreadDelegateImpl::OnWorkerBecomesIdleLockRequired( ScopedCommandsExecutor* executor, WorkerThread* worker) { DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_); - DCHECK(!outer_->idle_workers_stack_.Contains(worker)); - - if (outer_->after_start().no_worker_reclaim) { - // Under NoWorkerThreadReclaim, immediately cleanup excess workers that were - // going to sleep. Only 1 worker is cleaned up at a time, which is enough - // because each worker who caused |max_tasks_| to increase will eventually - // go through this. - if (outer_->workers_.size() > outer_->max_tasks_) { - CleanupLockRequired(executor, worker); - return; - } - } + DCHECK(!outer_->idle_workers_set_.Contains(worker)); - // Add the worker to the idle stack. - outer_->idle_workers_stack_.Push(worker); - DCHECK_LE(outer_->idle_workers_stack_.Size(), outer_->workers_.size()); - outer_->idle_workers_stack_cv_for_testing_->Broadcast(); + // Add the worker to the idle set. + outer_->idle_workers_set_.Insert(worker); + DCHECK_LE(outer_->idle_workers_set_.Size(), outer_->workers_.size()); + outer_->idle_workers_set_cv_for_testing_->Broadcast(); } void ThreadGroupImpl::WorkerThreadDelegateImpl::OnMainExit( @@ -746,12 +750,12 @@ void ThreadGroupImpl::WorkerThreadDelegateImpl::OnMainExit( bool shutdown_complete = outer_->task_tracker_->IsShutdownComplete(); CheckedAutoLock auto_lock(outer_->lock_); - // |worker| should already have been removed from the idle workers stack and + // |worker| should already have been removed from the idle workers set and // |workers_| by the time the thread is about to exit. (except in the cases // where the thread group is no longer going to be used - in which case, // it's fine for there to be invalid workers in the thread group. if (!shutdown_complete && !outer_->join_for_testing_started_) { - DCHECK(!outer_->idle_workers_stack_.Contains(worker)); + DCHECK(!outer_->idle_workers_set_.Contains(worker)); DCHECK(!ContainsWorker(outer_->workers_, worker)); } } @@ -775,6 +779,15 @@ void ThreadGroupImpl::WorkerThreadDelegateImpl::OnMainExit( outer_->num_workers_cleaned_up_for_testing_cv_->Signal(); } +void ThreadGroupImpl::WorkerThreadDelegateImpl::RecordUnnecessaryWakeup() { + base::BooleanHistogram::FactoryGet( + std::string("ThreadPool.UnnecessaryWakeup.") + outer_->histogram_label_, + base::Histogram::kUmaTargetedHistogramFlag) + ->Add(true); + + TRACE_EVENT_INSTANT("wakeup.flow", "ThreadPool.UnnecessaryWakeup"); +} + void ThreadGroupImpl::WorkerThreadDelegateImpl::BlockingStarted( BlockingType blocking_type) { DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_); @@ -881,35 +894,21 @@ void ThreadGroupImpl::WorkerThreadDelegateImpl::OnShutdownStartedLockRequired( bool ThreadGroupImpl::WorkerThreadDelegateImpl::CanGetWorkLockRequired( ScopedCommandsExecutor* executor, WorkerThread* worker) { - const bool is_on_idle_workers_stack = - outer_->IsOnIdleStackLockRequired(worker); - DCHECK_EQ(is_on_idle_workers_stack, - outer_->idle_workers_stack_.Contains(worker)); - - if (outer_->after_start().no_worker_reclaim) { - DCHECK(!is_on_idle_workers_stack); - // Under NoWorkerThreadReclaim, immediately cleanup excess workers. Only 1 - // worker is cleaned up at a time, which is enough because each worker who - // caused |max_tasks_| to increase will eventually go through this. - if (outer_->GetNumAwakeWorkersLockRequired() > outer_->max_tasks_) { + const bool is_on_idle_workers_set = outer_->IsOnIdleSetLockRequired(worker); + DCHECK_EQ(is_on_idle_workers_set, outer_->idle_workers_set_.Contains(worker)); + + if (is_on_idle_workers_set) { + if (CanCleanupLockRequired(worker)) CleanupLockRequired(executor, worker); - return false; - } - } else { - if (is_on_idle_workers_stack) { - if (CanCleanupLockRequired(worker)) - CleanupLockRequired(executor, worker); - return false; - } + return false; + } - // Excess workers should not get work, until they are no longer excess (i.e. - // max tasks increases). This ensures that if we have excess workers in the - // thread group, they get a chance to no longer be excess before being - // cleaned up. - if (outer_->GetNumAwakeWorkersLockRequired() > outer_->max_tasks_) { - OnWorkerBecomesIdleLockRequired(executor, worker); - return false; - } + // If too many workers are running, this worker should not get work, until + // tasks are no longer in excess (i.e. max tasks increases). This ensures that + // if this worker is in excess, it gets a chance to being cleaned up. + if (outer_->GetNumAwakeWorkersLockRequired() > outer_->max_tasks_) { + OnWorkerBecomesIdleLockRequired(executor, worker); + return false; } return true; @@ -950,8 +949,8 @@ void ThreadGroupImpl::WaitForWorkersIdleLockRequiredForTesting(size_t n) { // Make sure workers do not cleanup while watching the idle count. AutoReset<bool> ban_cleanups(&worker_cleanup_disallowed_for_testing_, true); - while (idle_workers_stack_.Size() < n) - idle_workers_stack_cv_for_testing_->Wait(); + while (idle_workers_set_.Size() < n) + idle_workers_set_cv_for_testing_->Wait(); } void ThreadGroupImpl::MaintainAtLeastOneIdleWorkerLockRequired( @@ -960,7 +959,7 @@ void ThreadGroupImpl::MaintainAtLeastOneIdleWorkerLockRequired( return; DCHECK_LT(workers_.size(), kMaxNumberOfWorkers); - if (!idle_workers_stack_.IsEmpty()) + if (!idle_workers_set_.IsEmpty()) return; if (workers_.size() >= max_tasks_) @@ -969,7 +968,7 @@ void ThreadGroupImpl::MaintainAtLeastOneIdleWorkerLockRequired( scoped_refptr<WorkerThread> new_worker = CreateAndRegisterWorkerLockRequired(executor); DCHECK(new_worker); - idle_workers_stack_.Push(new_worker.get()); + idle_workers_set_.Insert(new_worker.get()); } scoped_refptr<WorkerThread> @@ -978,16 +977,19 @@ ThreadGroupImpl::CreateAndRegisterWorkerLockRequired( DCHECK(!join_for_testing_started_); DCHECK_LT(workers_.size(), max_tasks_); DCHECK_LT(workers_.size(), kMaxNumberOfWorkers); - DCHECK(idle_workers_stack_.IsEmpty()); + DCHECK(idle_workers_set_.IsEmpty()); // WorkerThread needs |lock_| as a predecessor for its thread lock // because in WakeUpOneWorker, |lock_| is first acquired and then // the thread lock is acquired when WakeUp is called on the worker. - scoped_refptr<WorkerThread> worker = - MakeRefCounted<WorkerThread>(thread_type_hint_, - std::make_unique<WorkerThreadDelegateImpl>( - tracked_ref_factory_.GetTrackedRef()), - task_tracker_, &lock_); + scoped_refptr<WorkerThread> worker = MakeRefCounted<WorkerThread>( + thread_type_hint_, + std::make_unique<WorkerThreadDelegateImpl>( + tracked_ref_factory_.GetTrackedRef(), + /* is_excess=*/after_start().no_worker_reclaim + ? workers_.size() >= after_start().initial_max_tasks + : true), + task_tracker_, worker_sequence_num_++, &lock_); workers_.push_back(worker); executor->ScheduleStart(worker); @@ -997,8 +999,8 @@ ThreadGroupImpl::CreateAndRegisterWorkerLockRequired( } size_t ThreadGroupImpl::GetNumAwakeWorkersLockRequired() const { - DCHECK_GE(workers_.size(), idle_workers_stack_.Size()); - size_t num_awake_workers = workers_.size() - idle_workers_stack_.Size(); + DCHECK_GE(workers_.size(), idle_workers_set_.Size()); + size_t num_awake_workers = workers_.size() - idle_workers_set_.Size(); DCHECK_GE(num_awake_workers, num_running_tasks_); return num_awake_workers; } @@ -1076,7 +1078,7 @@ void ThreadGroupImpl::EnsureEnoughWorkersLockRequired( // Wake up the appropriate number of workers. for (size_t i = 0; i < num_workers_to_wake_up; ++i) { MaintainAtLeastOneIdleWorkerLockRequired(executor); - WorkerThread* worker_to_wakeup = idle_workers_stack_.Pop(); + WorkerThread* worker_to_wakeup = idle_workers_set_.Take(); DCHECK(worker_to_wakeup); executor->ScheduleWakeUp(worker_to_wakeup); } @@ -1179,11 +1181,11 @@ void ThreadGroupImpl::UpdateMinAllowedPriorityLockRequired() { } } -bool ThreadGroupImpl::IsOnIdleStackLockRequired(WorkerThread* worker) const { - // To avoid searching through the idle stack : use GetLastUsedTime() not being - // null (or being directly on top of the idle stack) as a proxy for being on - // the idle stack. - return idle_workers_stack_.Peek() == worker || +bool ThreadGroupImpl::IsOnIdleSetLockRequired(WorkerThread* worker) const { + // To avoid searching through the idle set : use GetLastUsedTime() not being + // null (or being directly on top of the idle set) as a proxy for being on + // the idle set. + return idle_workers_set_.Peek() == worker || !worker->GetLastUsedTime().is_null(); } diff --git a/chromium/base/task/thread_pool/thread_group_impl.h b/chromium/base/task/thread_pool/thread_group_impl.h index b905ced66bc..2c5d52db86b 100644 --- a/chromium/base/task/thread_pool/thread_group_impl.h +++ b/chromium/base/task/thread_pool/thread_group_impl.h @@ -27,7 +27,7 @@ #include "base/task/thread_pool/thread_group.h" #include "base/task/thread_pool/tracked_ref.h" #include "base/task/thread_pool/worker_thread.h" -#include "base/task/thread_pool/worker_thread_stack.h" +#include "base/task/thread_pool/worker_thread_set.h" #include "base/time/time.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -59,7 +59,8 @@ class BASE_EXPORT ThreadGroupImpl : public ThreadGroup { StringPiece thread_group_label, ThreadType thread_type_hint, TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate); + TrackedRef<Delegate> delegate, + ThreadGroup* predecessor_thread_group = nullptr); // Creates threads, allowing existing and future tasks to run. The thread // group runs at most |max_tasks| / `max_best_effort_tasks` unblocked task @@ -104,7 +105,7 @@ class BASE_EXPORT ThreadGroupImpl : public ThreadGroup { // disallowed from cleaning up during this call: tests using a custom // |suggested_reclaim_time_| need to be careful to invoke this swiftly after // unblocking the waited upon workers as: if a worker is already detached by - // the time this is invoked, it will never make it onto the idle stack and + // the time this is invoked, it will never make it onto the idle set and // this call will hang. void WaitForWorkersIdleForTesting(size_t n); @@ -164,7 +165,7 @@ class BASE_EXPORT ThreadGroupImpl : public ThreadGroup { scoped_refptr<WorkerThread> CreateAndRegisterWorkerLockRequired( ScopedCommandsExecutor* executor) EXCLUSIVE_LOCKS_REQUIRED(lock_); - // Returns the number of workers that are awake (i.e. not on the idle stack). + // Returns the number of workers that are awake (i.e. not on the idle set). size_t GetNumAwakeWorkersLockRequired() const EXCLUSIVE_LOCKS_REQUIRED(lock_); // Returns the desired number of awake workers, given current workload and @@ -208,7 +209,7 @@ class BASE_EXPORT ThreadGroupImpl : public ThreadGroup { // or when a new task is added to |priority_queue_|. void UpdateMinAllowedPriorityLockRequired() EXCLUSIVE_LOCKS_REQUIRED(lock_); - bool IsOnIdleStackLockRequired(WorkerThread* worker) const + bool IsOnIdleSetLockRequired(WorkerThread* worker) const EXCLUSIVE_LOCKS_REQUIRED(lock_); // Increments/decrements the number of tasks of |priority| that are currently @@ -274,11 +275,13 @@ class BASE_EXPORT ThreadGroupImpl : public ThreadGroup { return initialized_in_start_; } + const std::string histogram_label_; const std::string thread_group_label_; const ThreadType thread_type_hint_; // All workers owned by this thread group. std::vector<scoped_refptr<WorkerThread>> workers_ GUARDED_BY(lock_); + size_t worker_sequence_num_ GUARDED_BY(lock_) = 0; bool shutdown_started_ GUARDED_BY(lock_) = false; @@ -298,15 +301,16 @@ class BASE_EXPORT ThreadGroupImpl : public ThreadGroup { int num_unresolved_may_block_ GUARDED_BY(lock_) = 0; int num_unresolved_best_effort_may_block_ GUARDED_BY(lock_) = 0; - // Stack of idle workers. Initially, all workers are on this stack. A worker - // is removed from the stack before its WakeUp() function is called and when - // it receives work from GetWork() (a worker calls GetWork() when its sleep + // Ordered set of idle workers; the order uses pointer comparison, this is + // arbitrary but stable. Initially, all workers are on this set. A worker is + // removed from the set before its WakeUp() function is called and when it + // receives work from GetWork() (a worker calls GetWork() when its sleep // timeout expires, even if its WakeUp() method hasn't been called). A worker - // is pushed on this stack when it receives nullptr from GetWork(). - WorkerThreadStack idle_workers_stack_ GUARDED_BY(lock_); + // is inserted on this set when it receives nullptr from GetWork(). + WorkerThreadSet idle_workers_set_ GUARDED_BY(lock_); - // Signaled when a worker is added to the idle workers stack. - std::unique_ptr<ConditionVariable> idle_workers_stack_cv_for_testing_ + // Signaled when a worker is added to the idle workers set. + std::unique_ptr<ConditionVariable> idle_workers_set_cv_for_testing_ GUARDED_BY(lock_); // Whether an AdjustMaxTasks() task was posted to the service thread. diff --git a/chromium/base/task/thread_pool/thread_group_impl_unittest.cc b/chromium/base/task/thread_pool/thread_group_impl_unittest.cc index 9ce826c0fde..01a82feb290 100644 --- a/chromium/base/task/thread_pool/thread_group_impl_unittest.cc +++ b/chromium/base/task/thread_pool/thread_group_impl_unittest.cc @@ -79,6 +79,7 @@ class ThreadGroupImplImplTestBase : public ThreadGroup::Delegate { tracked_ref_factory_(this) {} void CommonTearDown() { + delayed_task_manager_.Shutdown(); service_thread_.Stop(); task_tracker_.FlushForTesting(); if (thread_group_) @@ -1533,7 +1534,7 @@ TEST_P(ThreadGroupImplOverCapacityTest, VerifyCleanup) { if (GetParam() == ReclaimType::DELAYED_RECLAIM) { // Note: one worker above capacity will not get cleaned up since it's on the - // top of the idle stack. + // front of the idle set. thread_group_->WaitForWorkersCleanedUpForTesting(kLocalMaxTasks - 1); EXPECT_EQ(kLocalMaxTasks + 1, thread_group_->NumberOfWorkersForTesting()); threads_continue.Signal(); diff --git a/chromium/base/task/thread_pool/thread_group_native.cc b/chromium/base/task/thread_pool/thread_group_native.cc deleted file mode 100644 index 24186986648..00000000000 --- a/chromium/base/task/thread_pool/thread_group_native.cc +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/task/thread_pool/thread_group_native.h" - -#include <algorithm> -#include <utility> - -#include "base/memory/raw_ptr.h" -#include "base/system/sys_info.h" -#include "base/task/thread_pool/task_tracker.h" - -namespace base { -namespace internal { - -class ThreadGroupNative::ScopedCommandsExecutor - : public ThreadGroup::BaseScopedCommandsExecutor { - public: - explicit ScopedCommandsExecutor(ThreadGroupNative* outer) : outer_(outer) {} - ScopedCommandsExecutor(const ScopedCommandsExecutor&) = delete; - ScopedCommandsExecutor& operator=(const ScopedCommandsExecutor&) = delete; - ~ScopedCommandsExecutor() { - CheckedLock::AssertNoLockHeldOnCurrentThread(); - - for (size_t i = 0; i < num_threadpool_work_to_submit_; ++i) - outer_->SubmitWork(); - } - - // Sets the number of threadpool work to submit upon destruction. - void set_num_threadpool_work_to_submit(size_t num) { - DCHECK_EQ(num_threadpool_work_to_submit_, 0U); - num_threadpool_work_to_submit_ = num; - } - - private: - const raw_ptr<ThreadGroupNative> outer_; - size_t num_threadpool_work_to_submit_ = 0; -}; - -ThreadGroupNative::ThreadGroupNative(TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate, - ThreadGroup* predecessor_thread_group) - : ThreadGroup(std::move(task_tracker), - std::move(delegate), - predecessor_thread_group) {} - -ThreadGroupNative::~ThreadGroupNative() { -#if DCHECK_IS_ON() - // Verify join_for_testing has been called to ensure that there is no more - // outstanding work. Otherwise, work may try to de-reference an invalid - // pointer to this class. - DCHECK(join_for_testing_returned_); -#endif -} - -void ThreadGroupNative::Start(WorkerEnvironment worker_environment) { - ThreadGroup::Start(); - - worker_environment_ = worker_environment; - - StartImpl(); - - ScopedCommandsExecutor executor(this); - CheckedAutoLock auto_lock(lock_); - DCHECK(!started_); - started_ = true; - EnsureEnoughWorkersLockRequired(&executor); -} - -void ThreadGroupNative::JoinForTesting() { - { - CheckedAutoLock auto_lock(lock_); - priority_queue_.EnableFlushTaskSourcesOnDestroyForTesting(); - } - - JoinImpl(); -#if DCHECK_IS_ON() - DCHECK(!join_for_testing_returned_); - join_for_testing_returned_ = true; -#endif -} - -void ThreadGroupNative::RunNextTaskSourceImpl() { - RegisteredTaskSource task_source = GetWork(); - - if (task_source) { - BindToCurrentThread(); - task_source = task_tracker_->RunAndPopNextTask(std::move(task_source)); - UnbindFromCurrentThread(); - - if (task_source) { - ScopedCommandsExecutor workers_executor(this); - ScopedReenqueueExecutor reenqueue_executor; - auto transaction_with_task_source = - TransactionWithRegisteredTaskSource::FromTaskSource( - std::move(task_source)); - CheckedAutoLock auto_lock(lock_); - ReEnqueueTaskSourceLockRequired(&workers_executor, &reenqueue_executor, - std::move(transaction_with_task_source)); - } - } -} - -void ThreadGroupNative::UpdateMinAllowedPriorityLockRequired() { - // Tasks should yield as soon as there is work of higher priority in - // |priority_queue_|. - if (priority_queue_.IsEmpty()) { - max_allowed_sort_key_.store(kMaxYieldSortKey, std::memory_order_relaxed); - } else { - max_allowed_sort_key_.store({priority_queue_.PeekSortKey().priority(), - priority_queue_.PeekSortKey().worker_count()}, - std::memory_order_relaxed); - } -} - -RegisteredTaskSource ThreadGroupNative::GetWork() { - ScopedCommandsExecutor workers_executor(this); - CheckedAutoLock auto_lock(lock_); - DCHECK_GT(num_pending_threadpool_work_, 0U); - --num_pending_threadpool_work_; - - RegisteredTaskSource task_source; - TaskPriority priority; - while (!task_source && !priority_queue_.IsEmpty()) { - priority = priority_queue_.PeekSortKey().priority(); - // Enforce the CanRunPolicy. - if (!task_tracker_->CanRunPriority(priority)) - return nullptr; - - task_source = TakeRegisteredTaskSource(&workers_executor); - } - UpdateMinAllowedPriorityLockRequired(); - return task_source; -} - -void ThreadGroupNative::UpdateSortKey(TaskSource::Transaction transaction) { - ScopedCommandsExecutor executor(this); - UpdateSortKeyImpl(&executor, std::move(transaction)); -} - -void ThreadGroupNative::PushTaskSourceAndWakeUpWorkers( - TransactionWithRegisteredTaskSource transaction_with_task_source) { - ScopedCommandsExecutor executor(this); - PushTaskSourceAndWakeUpWorkersImpl(&executor, - std::move(transaction_with_task_source)); -} - -void ThreadGroupNative::EnsureEnoughWorkersLockRequired( - BaseScopedCommandsExecutor* executor) { - if (!started_) - return; - // Ensure that there is at least one pending threadpool work per TaskSource in - // the PriorityQueue. - const size_t desired_num_pending_threadpool_work = - GetNumAdditionalWorkersForBestEffortTaskSourcesLockRequired() + - GetNumAdditionalWorkersForForegroundTaskSourcesLockRequired(); - - if (desired_num_pending_threadpool_work > num_pending_threadpool_work_) { - static_cast<ScopedCommandsExecutor*>(executor) - ->set_num_threadpool_work_to_submit( - desired_num_pending_threadpool_work - num_pending_threadpool_work_); - num_pending_threadpool_work_ = desired_num_pending_threadpool_work; - } - // This function is called every time a task source is queued or re-enqueued, - // hence the minimum priority needs to be updated. - UpdateMinAllowedPriorityLockRequired(); -} - -size_t ThreadGroupNative::GetMaxConcurrentNonBlockedTasksDeprecated() const { - // Native thread pools give us no control over the number of workers that are - // active at one time. Consequently, we cannot report a true value here. - // Instead, the values were chosen to match - // ThreadPoolInstance::StartWithDefaultParams. - return static_cast<size_t>(std::max(3, SysInfo::NumberOfProcessors() - 1)); -} - -void ThreadGroupNative::DidUpdateCanRunPolicy() { - ScopedCommandsExecutor executor(this); - CheckedAutoLock auto_lock(lock_); - EnsureEnoughWorkersLockRequired(&executor); -} - -void ThreadGroupNative::OnShutdownStarted() {} - -} // namespace internal -} // namespace base diff --git a/chromium/base/task/thread_pool/thread_group_native.h b/chromium/base/task/thread_pool/thread_group_native.h deleted file mode 100644 index b5e7be6befa..00000000000 --- a/chromium/base/task/thread_pool/thread_group_native.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TASK_THREAD_POOL_THREAD_GROUP_NATIVE_H_ -#define BASE_TASK_THREAD_POOL_THREAD_GROUP_NATIVE_H_ - -#include "base/base_export.h" -#include "base/dcheck_is_on.h" -#include "base/synchronization/atomic_flag.h" -#include "base/task/thread_pool/thread_group.h" - -namespace base { -namespace internal { - -class BASE_EXPORT ThreadGroupNative : public ThreadGroup { - public: - ThreadGroupNative(const ThreadGroupNative&) = delete; - ThreadGroupNative& operator=(const ThreadGroupNative&) = delete; - - // Destroying a ThreadGroupNative is not allowed in - // production; it is always leaked. In tests, it can only be destroyed after - // JoinForTesting() has returned. - ~ThreadGroupNative() override; - - // Starts the thread group and allows tasks to begin running. - void Start(WorkerEnvironment worker_environment = WorkerEnvironment::NONE); - - // ThreadGroup: - void JoinForTesting() override; - size_t GetMaxConcurrentNonBlockedTasksDeprecated() const override; - void DidUpdateCanRunPolicy() override; - void OnShutdownStarted() override; - - protected: - ThreadGroupNative(TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate, - ThreadGroup* predecessor_thread_group); - - // Runs a task off the next task source on the |priority_queue_|. Called by - // callbacks posted to platform native thread pools. - void RunNextTaskSourceImpl(); - - virtual void JoinImpl() = 0; - virtual void StartImpl() = 0; - virtual void SubmitWork() = 0; - - // Used to control the worker environment. Supports COM MTA on Windows. - WorkerEnvironment worker_environment_ = WorkerEnvironment::NONE; - - private: - class ScopedCommandsExecutor; - - // ThreadGroup: - void UpdateSortKey(TaskSource::Transaction transaction) override; - void PushTaskSourceAndWakeUpWorkers( - TransactionWithRegisteredTaskSource transaction_with_task_source) - override; - void EnsureEnoughWorkersLockRequired(BaseScopedCommandsExecutor* executor) - override EXCLUSIVE_LOCKS_REQUIRED(lock_); - - // Updates the minimum priority allowed to run below which tasks should yield, - // based on task sources in |priority_queue_|. - void UpdateMinAllowedPriorityLockRequired() EXCLUSIVE_LOCKS_REQUIRED(lock_); - - // Returns the top TaskSource off the |priority_queue_|. Returns nullptr - // if the |priority_queue_| is empty. - RegisteredTaskSource GetWork(); - - // Indicates whether the thread group has been started yet. - bool started_ GUARDED_BY(lock_) = false; - - // Number of threadpool work submitted to the thread group which haven't - // popped a TaskSource from the PriorityQueue yet. - size_t num_pending_threadpool_work_ GUARDED_BY(lock_) = 0; - -#if DCHECK_IS_ON() - // Set once JoinForTesting() has returned. - bool join_for_testing_returned_ = false; -#endif -}; - -} // namespace internal -} // namespace base - -#endif // BASE_TASK_THREAD_POOL_THREAD_GROUP_NATIVE_H_ diff --git a/chromium/base/task/thread_pool/thread_group_native_mac.h b/chromium/base/task/thread_pool/thread_group_native_mac.h deleted file mode 100644 index 94f771df5f7..00000000000 --- a/chromium/base/task/thread_pool/thread_group_native_mac.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TASK_THREAD_POOL_THREAD_GROUP_NATIVE_MAC_H_ -#define BASE_TASK_THREAD_POOL_THREAD_GROUP_NATIVE_MAC_H_ - -#include <dispatch/dispatch.h> - -#include "base/base_export.h" -#include "base/mac/scoped_dispatch_object.h" -#include "base/task/thread_pool/thread_group_native.h" -#include "base/threading/platform_thread.h" - -namespace base { -namespace internal { - -// A ThreadGroup implementation backed by libdispatch. -// -// libdispatch official documentation: -// https://developer.apple.com/documentation/dispatch -// -// Guides: -// https://apple.github.io/swift-corelibs-libdispatch/tutorial/ -// https://developer.apple.com/library/archive/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html -class BASE_EXPORT ThreadGroupNativeMac : public ThreadGroupNative { - public: - // `io_thread_task_runner` is used to setup FileDescriptorWatcher on worker - // threads. `io_thread_task_runner` must refer to a Thread with - // MessgaePumpType::IO - ThreadGroupNativeMac( - ThreadType thread_type_hint, - scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner, - TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate, - ThreadGroup* predecessor_thread_group = nullptr); - - ThreadGroupNativeMac(const ThreadGroupNativeMac&) = delete; - ThreadGroupNativeMac& operator=(const ThreadGroupNativeMac&) = delete; - ~ThreadGroupNativeMac() override; - - private: - // ThreadGroupNative: - void JoinImpl() override; - void StartImpl() override; - void SubmitWork() override; - - const ThreadType thread_type_hint_; - - // Dispatch queue on which work is scheduled. Backed by a shared thread pool - // managed by libdispatch. - ScopedDispatchObject<dispatch_queue_t> queue_; - - // Dispatch group to enable synchronization. - ScopedDispatchObject<dispatch_group_t> group_; - - // Service thread task runner. - scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner_; -}; - -using ThreadGroupNativeImpl = ThreadGroupNativeMac; - -} // namespace internal -} // namespace base - -#endif // BASE_TASK_THREAD_POOL_THREAD_GROUP_NATIVE_MAC_H_ diff --git a/chromium/base/task/thread_pool/thread_group_native_mac.mm b/chromium/base/task/thread_pool/thread_group_native_mac.mm deleted file mode 100644 index e650f3a003d..00000000000 --- a/chromium/base/task/thread_pool/thread_group_native_mac.mm +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/task/thread_pool/thread_group_native_mac.h" -#include "base/files/file_descriptor_watcher_posix.h" - -#include "base/check.h" -#include "base/task/thread_pool/task_tracker.h" - -namespace base { -namespace internal { - -ThreadGroupNativeMac::ThreadGroupNativeMac( - ThreadType thread_type_hint, - scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner, - TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate, - ThreadGroup* predecessor_thread_group) - : ThreadGroupNative(std::move(task_tracker), - std::move(delegate), - predecessor_thread_group), - thread_type_hint_(thread_type_hint), - io_thread_task_runner_(std::move(io_thread_task_runner)) { - // The thread group only support kNormal or kBackground type. - DCHECK(thread_type_hint_ == ThreadType::kDefault || - thread_type_hint_ == ThreadType::kBackground); - DCHECK(io_thread_task_runner_); -} - -ThreadGroupNativeMac::~ThreadGroupNativeMac() {} - -void ThreadGroupNativeMac::StartImpl() { - dispatch_queue_attr_t attributes = dispatch_queue_attr_make_with_qos_class( - DISPATCH_QUEUE_CONCURRENT, - thread_type_hint_ == ThreadType::kDefault ? QOS_CLASS_USER_INITIATED - : QOS_CLASS_BACKGROUND, - /*relative_priority=*/-1); - queue_.reset(dispatch_queue_create("org.chromium.base.ThreadPool.ThreadGroup", - attributes)); - group_.reset(dispatch_group_create()); -} - -void ThreadGroupNativeMac::JoinImpl() { - dispatch_group_wait(group_, DISPATCH_TIME_FOREVER); -} - -void ThreadGroupNativeMac::SubmitWork() { - dispatch_group_async(group_, queue_, ^{ - FileDescriptorWatcher file_descriptor_watcher(io_thread_task_runner_); - RunNextTaskSourceImpl(); - }); -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/task/thread_pool/thread_group_native_win.cc b/chromium/base/task/thread_pool/thread_group_native_win.cc deleted file mode 100644 index f53521b8ae0..00000000000 --- a/chromium/base/task/thread_pool/thread_group_native_win.cc +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/task/thread_pool/thread_group_native_win.h" - -#include <utility> - -#include "base/task/thread_pool/task_tracker.h" -#include "base/threading/scoped_blocking_call_internal.h" -#include "base/win/scoped_com_initializer.h" - -namespace base { -namespace internal { - -class ThreadGroupNativeWin::ScopedCallbackMayRunLongObserver - : public BlockingObserver { - public: - explicit ScopedCallbackMayRunLongObserver(PTP_CALLBACK_INSTANCE callback) - : callback_(callback) { - SetBlockingObserverForCurrentThread(this); - } - - ScopedCallbackMayRunLongObserver(const ScopedCallbackMayRunLongObserver&) = - delete; - ScopedCallbackMayRunLongObserver& operator=( - const ScopedCallbackMayRunLongObserver&) = delete; - - ~ScopedCallbackMayRunLongObserver() override { - ClearBlockingObserverForCurrentThread(); - } - - // BlockingObserver: - void BlockingStarted(BlockingType blocking_type) override { - ::CallbackMayRunLong(callback_); - // CallbackMayRunLong should not be called twice. - ClearBlockingObserverForCurrentThread(); - } - - void BlockingTypeUpgraded() override {} - void BlockingEnded() override {} - - private: - PTP_CALLBACK_INSTANCE callback_; -}; - -ThreadGroupNativeWin::ThreadGroupNativeWin( - TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate, - ThreadGroup* predecessor_thread_group) - : ThreadGroupNative(std::move(task_tracker), - std::move(delegate), - predecessor_thread_group) {} - -ThreadGroupNativeWin::~ThreadGroupNativeWin() { - ::DestroyThreadpoolEnvironment(&environment_); - ::CloseThreadpoolWork(work_); - ::CloseThreadpool(pool_); -} - -void ThreadGroupNativeWin::StartImpl() { - ::InitializeThreadpoolEnvironment(&environment_); - - pool_ = ::CreateThreadpool(nullptr); - DCHECK(pool_) << "LastError: " << ::GetLastError(); - ::SetThreadpoolThreadMinimum(pool_, 1); - ::SetThreadpoolThreadMaximum(pool_, 256); - - work_ = ::CreateThreadpoolWork(&RunNextTaskSource, this, &environment_); - DCHECK(work_) << "LastError: " << GetLastError(); - ::SetThreadpoolCallbackPool(&environment_, pool_); -} - -void ThreadGroupNativeWin::JoinImpl() { - ::WaitForThreadpoolWorkCallbacks(work_, true); -} - -void ThreadGroupNativeWin::SubmitWork() { - // TODO(fdoray): Handle priorities by having different work objects and using - // SetThreadpoolCallbackPriority(). - ::SubmitThreadpoolWork(work_); -} - -// static -void CALLBACK -ThreadGroupNativeWin::RunNextTaskSource(PTP_CALLBACK_INSTANCE callback_instance, - void* thread_group_windows_impl, - PTP_WORK) { - auto* thread_group = - static_cast<ThreadGroupNativeWin*>(thread_group_windows_impl); - - // Windows Thread Pool API best practices state that all resources created - // in the callback function should be cleaned up before returning from the - // function. This includes COM initialization. - auto win_thread_environment = thread_group->GetScopedWindowsThreadEnvironment( - thread_group->worker_environment_); - - ScopedCallbackMayRunLongObserver callback_may_run_long_observer( - callback_instance); - - thread_group->RunNextTaskSourceImpl(); -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/task/thread_pool/thread_group_native_win.h b/chromium/base/task/thread_pool/thread_group_native_win.h deleted file mode 100644 index 16e8a308df7..00000000000 --- a/chromium/base/task/thread_pool/thread_group_native_win.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2017 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TASK_THREAD_POOL_THREAD_GROUP_NATIVE_WIN_H_ -#define BASE_TASK_THREAD_POOL_THREAD_GROUP_NATIVE_WIN_H_ - -#include <windows.h> - -#include "base/base_export.h" -#include "base/task/thread_pool/thread_group_native.h" - -namespace base { -namespace internal { - -// A ThreadGroup implementation backed by the Windows Thread Pool API. -// -// Windows Thread Pool API official documentation: -// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686766(v=vs.85).aspx -// -// Blog posts on the Windows Thread Pool API: -// https://msdn.microsoft.com/magazine/hh335066.aspx -// https://msdn.microsoft.com/magazine/hh394144.aspx -// https://msdn.microsoft.com/magazine/hh456398.aspx -// https://msdn.microsoft.com/magazine/hh547107.aspx -// https://msdn.microsoft.com/magazine/hh580731.aspx -class BASE_EXPORT ThreadGroupNativeWin : public ThreadGroupNative { - public: - ThreadGroupNativeWin(TrackedRef<TaskTracker> task_tracker, - TrackedRef<Delegate> delegate, - ThreadGroup* predecessor_thread_group = nullptr); - - ThreadGroupNativeWin(const ThreadGroupNativeWin&) = delete; - ThreadGroupNativeWin& operator=(const ThreadGroupNativeWin&) = delete; - - ~ThreadGroupNativeWin() override; - - private: - class ScopedCallbackMayRunLongObserver; - - // Callback that gets run by |pool_|. - static void CALLBACK - RunNextTaskSource(PTP_CALLBACK_INSTANCE callback_instance, - void* thread_group_windows_impl, - PTP_WORK); - - // ThreadGroupNative: - void JoinImpl() override; - void StartImpl() override; - void SubmitWork() override; - - // Thread pool object that |work_| gets executed on. - PTP_POOL pool_ = nullptr; - - // Callback environment. |pool_| is associated with |environment_| so that - // work objects using this environment run on |pool_|. - TP_CALLBACK_ENVIRON environment_ = {}; - - // Work object that executes RunNextTaskSource. It has a pointer to the - // current |ThreadGroupNativeWin| and a pointer to |environment_| bound - // to it. - PTP_WORK work_ = nullptr; -}; - -using ThreadGroupNativeImpl = ThreadGroupNativeWin; - -} // namespace internal -} // namespace base - -#endif // BASE_TASK_THREAD_POOL_THREAD_GROUP_NATIVE_WIN_H_ diff --git a/chromium/base/task/thread_pool/thread_group_unittest.cc b/chromium/base/task/thread_pool/thread_group_unittest.cc index d02abef5f5c..233c84b0d86 100644 --- a/chromium/base/task/thread_pool/thread_group_unittest.cc +++ b/chromium/base/task/thread_pool/thread_group_unittest.cc @@ -35,11 +35,8 @@ #include "testing/gtest/include/gtest/gtest.h" #if BUILDFLAG(IS_WIN) -#include "base/task/thread_pool/thread_group_native_win.h" #include "base/win/com_init_check_hook.h" #include "base/win/com_init_util.h" -#elif BUILDFLAG(IS_APPLE) -#include "base/task/thread_pool/thread_group_native_mac.h" #endif namespace base { @@ -47,15 +44,6 @@ namespace internal { namespace { -#if HAS_NATIVE_THREAD_POOL() -using ThreadGroupNativeType = -#if BUILDFLAG(IS_WIN) - ThreadGroupNativeWin; -#elif BUILDFLAG(IS_APPLE) - ThreadGroupNativeMac; -#endif -#endif - constexpr size_t kMaxTasks = 4; constexpr size_t kTooManyTasks = 1000; // By default, tests allow half of the thread group to be used by best-effort @@ -113,6 +101,7 @@ class ThreadGroupTestBase : public testing::Test, public ThreadGroup::Delegate { } void TearDown() override { + delayed_task_manager_.Shutdown(); service_thread_.Stop(); if (thread_group_) thread_group_->JoinForTesting(); @@ -121,25 +110,9 @@ class ThreadGroupTestBase : public testing::Test, public ThreadGroup::Delegate { void CreateThreadGroup() { ASSERT_FALSE(thread_group_); - switch (GetGroupType()) { - case test::GroupType::GENERIC: - thread_group_ = std::make_unique<ThreadGroupImpl>( - "TestThreadGroup", "A", ThreadType::kDefault, - task_tracker_.GetTrackedRef(), - tracked_ref_factory_.GetTrackedRef()); - break; -#if HAS_NATIVE_THREAD_POOL() - case test::GroupType::NATIVE: - thread_group_ = std::make_unique<ThreadGroupNativeType>( -#if BUILDFLAG(IS_APPLE) - ThreadType::kDefault, service_thread_.task_runner(), -#endif - task_tracker_.GetTrackedRef(), - tracked_ref_factory_.GetTrackedRef()); - break; -#endif - } - ASSERT_TRUE(thread_group_); + thread_group_ = std::make_unique<ThreadGroupImpl>( + "TestThreadGroup", "A", ThreadType::kDefault, + task_tracker_.GetTrackedRef(), tracked_ref_factory_.GetTrackedRef()); mock_pooled_task_runner_delegate_.SetThreadGroup(thread_group_.get()); } @@ -147,28 +120,13 @@ class ThreadGroupTestBase : public testing::Test, public ThreadGroup::Delegate { void StartThreadGroup(ThreadGroup::WorkerEnvironment worker_environment = ThreadGroup::WorkerEnvironment::NONE) { ASSERT_TRUE(thread_group_); - switch (GetGroupType()) { - case test::GroupType::GENERIC: { - ThreadGroupImpl* thread_group_impl = - static_cast<ThreadGroupImpl*>(thread_group_.get()); - thread_group_impl->Start( - kMaxTasks, kMaxBestEffortTasks, TimeDelta::Max(), - service_thread_.task_runner(), nullptr, worker_environment); - break; - } -#if HAS_NATIVE_THREAD_POOL() - case test::GroupType::NATIVE: { - ThreadGroupNativeType* thread_group_native_impl = - static_cast<ThreadGroupNativeType*>(thread_group_.get()); - thread_group_native_impl->Start(worker_environment); - break; - } -#endif - } + ThreadGroupImpl* thread_group_impl = + static_cast<ThreadGroupImpl*>(thread_group_.get()); + thread_group_impl->Start(kMaxTasks, kMaxBestEffortTasks, TimeDelta::Max(), + service_thread_.task_runner(), nullptr, + worker_environment); } - virtual test::GroupType GetGroupType() const = 0; - Thread service_thread_{"ThreadPoolServiceThread"}; TaskTracker task_tracker_; DelayedTaskManager delayed_task_manager_; @@ -186,22 +144,13 @@ class ThreadGroupTestBase : public testing::Test, public ThreadGroup::Delegate { TrackedRefFactory<ThreadGroup::Delegate> tracked_ref_factory_{this}; }; -class ThreadGroupTest : public ThreadGroupTestBase, - public testing::WithParamInterface<test::GroupType> { - public: - ThreadGroupTest() = default; - ThreadGroupTest(const ThreadGroupTest&) = delete; - ThreadGroupTest& operator=(const ThreadGroupTest&) = delete; - - test::GroupType GetGroupType() const override { return GetParam(); } -}; +using ThreadGroupTest = ThreadGroupTestBase; // TODO(etiennep): Audit tests that don't need TaskSourceExecutionMode // parameter. class ThreadGroupTestAllExecutionModes : public ThreadGroupTestBase, - public testing::WithParamInterface< - std::tuple<test::GroupType, TaskSourceExecutionMode>> { + public testing::WithParamInterface<TaskSourceExecutionMode> { public: ThreadGroupTestAllExecutionModes() = default; ThreadGroupTestAllExecutionModes(const ThreadGroupTestAllExecutionModes&) = @@ -209,13 +158,7 @@ class ThreadGroupTestAllExecutionModes ThreadGroupTestAllExecutionModes& operator=( const ThreadGroupTestAllExecutionModes&) = delete; - test::GroupType GetGroupType() const override { - return std::get<0>(GetParam()); - } - - TaskSourceExecutionMode execution_mode() const { - return std::get<1>(GetParam()); - } + TaskSourceExecutionMode execution_mode() const { return GetParam(); } scoped_refptr<TaskRunner> CreatePooledTaskRunner( const TaskTraits& traits = {}) { @@ -379,7 +322,7 @@ TEST_P(ThreadGroupTestAllExecutionModes, CanRunPolicyBasic) { &task_tracker_); } -TEST_P(ThreadGroupTest, CanRunPolicyUpdatedBeforeRun) { +TEST_F(ThreadGroupTest, CanRunPolicyUpdatedBeforeRun) { StartThreadGroup(); // This test only works with SequencedTaskRunner become it assumes // ordered execution of 2 posted tasks. @@ -404,7 +347,7 @@ TEST_P(ThreadGroupTestAllExecutionModes, CanRunPolicyLoad) { // Verifies that ShouldYield() returns true for a priority that is not allowed // to run by the CanRunPolicy. -TEST_P(ThreadGroupTest, CanRunPolicyShouldYield) { +TEST_F(ThreadGroupTest, CanRunPolicyShouldYield) { StartThreadGroup(); task_tracker_.SetCanRunPolicy(CanRunPolicy::kNone); @@ -432,7 +375,7 @@ TEST_P(ThreadGroupTest, CanRunPolicyShouldYield) { // Verify that the maximum number of BEST_EFFORT tasks that can run concurrently // in a thread group does not affect Sequences with a priority that was // increased from BEST_EFFORT to USER_BLOCKING. -TEST_P(ThreadGroupTest, UpdatePriorityBestEffortToUserBlocking) { +TEST_F(ThreadGroupTest, UpdatePriorityBestEffortToUserBlocking) { StartThreadGroup(); CheckedLock num_tasks_running_lock; @@ -549,7 +492,7 @@ TEST_P(ThreadGroupTestAllExecutionModes, NoWorkerEnvironment) { #endif // Verifies that ShouldYield() returns false when there is no pending task. -TEST_P(ThreadGroupTest, ShouldYieldSingleTask) { +TEST_F(ThreadGroupTest, ShouldYieldSingleTask) { StartThreadGroup(); test::CreatePooledTaskRunner({TaskPriority::USER_BLOCKING}, @@ -567,7 +510,7 @@ TEST_P(ThreadGroupTest, ShouldYieldSingleTask) { } // Verify that tasks from a JobTaskSource run at the intended concurrency. -TEST_P(ThreadGroupTest, ScheduleJobTaskSource) { +TEST_F(ThreadGroupTest, ScheduleJobTaskSource) { StartThreadGroup(); TestWaitableEvent threads_running; @@ -603,7 +546,7 @@ TEST_P(ThreadGroupTest, ScheduleJobTaskSource) { } // Verify that tasks from a JobTaskSource run at the intended concurrency. -TEST_P(ThreadGroupTest, ScheduleJobTaskSourceMultipleTime) { +TEST_F(ThreadGroupTest, ScheduleJobTaskSourceMultipleTime) { StartThreadGroup(); TestWaitableEvent thread_running; @@ -643,7 +586,7 @@ TEST_P(ThreadGroupTest, ScheduleJobTaskSourceMultipleTime) { // Verify that Cancel() on a job stops running the worker task and causes // current workers to yield. -TEST_P(ThreadGroupTest, CancelJobTaskSource) { +TEST_F(ThreadGroupTest, CancelJobTaskSource) { StartThreadGroup(); CheckedLock tasks_running_lock; @@ -686,7 +629,7 @@ TEST_P(ThreadGroupTest, CancelJobTaskSource) { // Verify that calling JobTaskSource::NotifyConcurrencyIncrease() (re-)schedule // tasks with the intended concurrency. -TEST_P(ThreadGroupTest, JobTaskSourceConcurrencyIncrease) { +TEST_F(ThreadGroupTest, JobTaskSourceConcurrencyIncrease) { StartThreadGroup(); TestWaitableEvent threads_running_a; @@ -735,7 +678,7 @@ TEST_P(ThreadGroupTest, JobTaskSourceConcurrencyIncrease) { // Verify that a JobTaskSource that becomes empty while in the queue eventually // gets discarded. -TEST_P(ThreadGroupTest, ScheduleEmptyJobTaskSource) { +TEST_F(ThreadGroupTest, ScheduleEmptyJobTaskSource) { StartThreadGroup(); task_tracker_.SetCanRunPolicy(CanRunPolicy::kNone); @@ -765,7 +708,7 @@ TEST_P(ThreadGroupTest, ScheduleEmptyJobTaskSource) { // Verify that Join() on a job contributes to max concurrency and waits for all // workers to return. -TEST_P(ThreadGroupTest, JoinJobTaskSource) { +TEST_F(ThreadGroupTest, JoinJobTaskSource) { StartThreadGroup(); TestWaitableEvent threads_continue; @@ -795,7 +738,7 @@ TEST_P(ThreadGroupTest, JoinJobTaskSource) { // Verify that finishing work outside of a job unblocks workers with a stale // max concurrency. -TEST_P(ThreadGroupTest, JoinJobTaskSourceStaleConcurrency) { +TEST_F(ThreadGroupTest, JoinJobTaskSourceStaleConcurrency) { StartThreadGroup(); TestWaitableEvent thread_running; @@ -821,7 +764,7 @@ TEST_P(ThreadGroupTest, JoinJobTaskSourceStaleConcurrency) { } // Verify that cancelling a job unblocks workers with a stale max concurrency. -TEST_P(ThreadGroupTest, CancelJobTaskSourceWithStaleConcurrency) { +TEST_F(ThreadGroupTest, CancelJobTaskSourceWithStaleConcurrency) { StartThreadGroup(); TestWaitableEvent thread_running; @@ -843,7 +786,7 @@ TEST_P(ThreadGroupTest, CancelJobTaskSourceWithStaleConcurrency) { // Verify that the maximum number of BEST_EFFORT tasks that can run concurrently // in a thread group does not affect JobTaskSource with a priority that was // increased from BEST_EFFORT to USER_BLOCKING. -TEST_P(ThreadGroupTest, JobTaskSourceUpdatePriority) { +TEST_F(ThreadGroupTest, JobTaskSourceUpdatePriority) { StartThreadGroup(); CheckedLock num_tasks_running_lock; @@ -906,45 +849,16 @@ TEST_P(ThreadGroupTest, JobTaskSourceUpdatePriority) { task_tracker_.FlushForTesting(); } -INSTANTIATE_TEST_SUITE_P(Generic, - ThreadGroupTest, - ::testing::Values(test::GroupType::GENERIC)); -INSTANTIATE_TEST_SUITE_P( - GenericParallel, - ThreadGroupTestAllExecutionModes, - ::testing::Combine(::testing::Values(test::GroupType::GENERIC), - ::testing::Values(TaskSourceExecutionMode::kParallel))); +INSTANTIATE_TEST_SUITE_P(GenericParallel, + ThreadGroupTestAllExecutionModes, + ::testing::Values(TaskSourceExecutionMode::kParallel)); INSTANTIATE_TEST_SUITE_P( GenericSequenced, ThreadGroupTestAllExecutionModes, - ::testing::Combine(::testing::Values(test::GroupType::GENERIC), - ::testing::Values(TaskSourceExecutionMode::kSequenced))); -INSTANTIATE_TEST_SUITE_P( - GenericJob, - ThreadGroupTestAllExecutionModes, - ::testing::Combine(::testing::Values(test::GroupType::GENERIC), - ::testing::Values(TaskSourceExecutionMode::kJob))); - -#if HAS_NATIVE_THREAD_POOL() -INSTANTIATE_TEST_SUITE_P(Native, - ThreadGroupTest, - ::testing::Values(test::GroupType::NATIVE)); -INSTANTIATE_TEST_SUITE_P( - NativeParallel, - ThreadGroupTestAllExecutionModes, - ::testing::Combine(::testing::Values(test::GroupType::NATIVE), - ::testing::Values(TaskSourceExecutionMode::kParallel))); -INSTANTIATE_TEST_SUITE_P( - NativeSequenced, - ThreadGroupTestAllExecutionModes, - ::testing::Combine(::testing::Values(test::GroupType::NATIVE), - ::testing::Values(TaskSourceExecutionMode::kSequenced))); -INSTANTIATE_TEST_SUITE_P( - NativeJob, - ThreadGroupTestAllExecutionModes, - ::testing::Combine(::testing::Values(test::GroupType::NATIVE), - ::testing::Values(TaskSourceExecutionMode::kJob))); -#endif + ::testing::Values(TaskSourceExecutionMode::kSequenced)); +INSTANTIATE_TEST_SUITE_P(GenericJob, + ThreadGroupTestAllExecutionModes, + ::testing::Values(TaskSourceExecutionMode::kJob)); } // namespace internal } // namespace base diff --git a/chromium/base/task/thread_pool/thread_pool_impl.cc b/chromium/base/task/thread_pool/thread_pool_impl.cc index 036aba36352..ad911cdd90f 100644 --- a/chromium/base/task/thread_pool/thread_pool_impl.cc +++ b/chromium/base/task/thread_pool/thread_pool_impl.cc @@ -33,14 +33,6 @@ #include "build/build_config.h" #include "third_party/abseil-cpp/absl/types/optional.h" -#if BUILDFLAG(IS_WIN) -#include "base/task/thread_pool/thread_group_native_win.h" -#endif - -#if BUILDFLAG(IS_APPLE) -#include "base/task/thread_pool/thread_group_native_mac.h" -#endif - namespace base { namespace internal { @@ -49,6 +41,9 @@ namespace { constexpr EnvironmentParams kForegroundPoolEnvironmentParams{ "Foreground", base::ThreadType::kDefault}; +constexpr EnvironmentParams kUtilityPoolEnvironmentParams{ + "Utility", base::ThreadType::kUtility}; + constexpr EnvironmentParams kBackgroundPoolEnvironmentParams{ "Background", base::ThreadType::kBackground}; @@ -82,7 +77,8 @@ ThreadPoolImpl::ThreadPoolImpl(StringPiece histogram_label) ThreadPoolImpl::ThreadPoolImpl(StringPiece histogram_label, std::unique_ptr<TaskTrackerImpl> task_tracker) - : task_tracker_(std::move(task_tracker)), + : histogram_label_(histogram_label), + task_tracker_(std::move(task_tracker)), single_thread_task_runner_manager_(task_tracker_->GetTrackedRef(), &delayed_task_manager_), has_disable_best_effort_switch_(HasDisableBestEffortTasksSwitch()), @@ -117,6 +113,7 @@ ThreadPoolImpl::~ThreadPoolImpl() { // Reset thread groups to release held TrackedRefs, which block teardown. foreground_thread_group_.reset(); + utility_thread_group_.reset(); background_thread_group_.reset(); } @@ -145,34 +142,23 @@ void ThreadPoolImpl::Start(const ThreadPoolInstance::InitParams& init_params, if (g_synchronous_thread_start_for_testing) service_thread_.WaitUntilThreadStarted(); -#if HAS_NATIVE_THREAD_POOL() - if (FeatureList::IsEnabled(kUseNativeThreadPool)) { - std::unique_ptr<ThreadGroup> old_group = - std::move(foreground_thread_group_); - foreground_thread_group_ = std::make_unique<ThreadGroupNativeImpl>( -#if BUILDFLAG(IS_APPLE) - ThreadType::kDefault, service_thread_.task_runner(), -#endif + if (FeatureList::IsEnabled(kUseUtilityThreadGroup) && + CanUseUtilityThreadTypeForWorkerThread()) { + utility_thread_group_ = std::make_unique<ThreadGroupImpl>( + histogram_label_.empty() + ? std::string() + : JoinString( + {histogram_label_, kUtilityPoolEnvironmentParams.name_suffix}, + "."), + kUtilityPoolEnvironmentParams.name_suffix, + kUtilityPoolEnvironmentParams.thread_type_hint, task_tracker_->GetTrackedRef(), tracked_ref_factory_.GetTrackedRef(), - old_group.get()); - old_group->InvalidateAndHandoffAllTaskSourcesToOtherThreadGroup( foreground_thread_group_.get()); + foreground_thread_group_ + ->HandoffNonUserBlockingTaskSourcesToOtherThreadGroup( + utility_thread_group_.get()); } - if (FeatureList::IsEnabled(kUseBackgroundNativeThreadPool)) { - std::unique_ptr<ThreadGroup> old_group = - std::move(background_thread_group_); - background_thread_group_ = std::make_unique<ThreadGroupNativeImpl>( -#if BUILDFLAG(IS_APPLE) - ThreadType::kBackground, service_thread_.task_runner(), -#endif - task_tracker_->GetTrackedRef(), tracked_ref_factory_.GetTrackedRef(), - old_group.get()); - old_group->InvalidateAndHandoffAllTaskSourcesToOtherThreadGroup( - background_thread_group_.get()); - } -#endif - // Update the CanRunPolicy based on |has_disable_best_effort_switch_|. UpdateCanRunPolicy(); @@ -195,39 +181,31 @@ void ThreadPoolImpl::Start(const ThreadPoolInstance::InitParams& init_params, #endif } -#if HAS_NATIVE_THREAD_POOL() - if (FeatureList::IsEnabled(kUseNativeThreadPool)) { - static_cast<ThreadGroupNative*>(foreground_thread_group_.get()) - ->Start(worker_environment); - } else -#endif - { - // On platforms that can't use the background thread priority, best-effort - // tasks run in foreground pools. A cap is set on the number of best-effort - // tasks that can run in foreground pools to ensure that there is always - // room for incoming foreground tasks and to minimize the performance impact - // of best-effort tasks. - static_cast<ThreadGroupImpl*>(foreground_thread_group_.get()) - ->Start(init_params.max_num_foreground_threads, max_best_effort_tasks, + // On platforms that can't use the background thread priority, best-effort + // tasks run in foreground pools. A cap is set on the number of best-effort + // tasks that can run in foreground pools to ensure that there is always + // room for incoming foreground tasks and to minimize the performance impact + // of best-effort tasks. + static_cast<ThreadGroupImpl*>(foreground_thread_group_.get()) + ->Start(init_params.max_num_foreground_threads, max_best_effort_tasks, + init_params.suggested_reclaim_time, service_thread_task_runner, + worker_thread_observer, worker_environment, + g_synchronous_thread_start_for_testing); + + if (utility_thread_group_) { + static_cast<ThreadGroupImpl*>(utility_thread_group_.get()) + ->Start(init_params.max_num_utility_threads, max_best_effort_tasks, init_params.suggested_reclaim_time, service_thread_task_runner, worker_thread_observer, worker_environment, g_synchronous_thread_start_for_testing); } if (background_thread_group_) { -#if HAS_NATIVE_THREAD_POOL() - if (FeatureList::IsEnabled(kUseBackgroundNativeThreadPool)) { - static_cast<ThreadGroupNative*>(background_thread_group_.get()) - ->Start(worker_environment); - } else -#endif - { - static_cast<ThreadGroupImpl*>(background_thread_group_.get()) - ->Start(max_best_effort_tasks, max_best_effort_tasks, - init_params.suggested_reclaim_time, - service_thread_task_runner, worker_thread_observer, - worker_environment, g_synchronous_thread_start_for_testing); - } + static_cast<ThreadGroupImpl*>(background_thread_group_.get()) + ->Start(max_best_effort_tasks, max_best_effort_tasks, + init_params.suggested_reclaim_time, service_thread_task_runner, + worker_thread_observer, worker_environment, + g_synchronous_thread_start_for_testing); } started_ = true; @@ -250,7 +228,7 @@ bool ThreadPoolImpl::PostDelayedTask(const Location& from_here, // Post |task| as part of a one-off single-task Sequence. return PostTaskWithSequence( Task(from_here, std::move(task), TimeTicks::Now(), delay, - base::GetTaskLeeway()), + GetDefaultTaskLeeway()), MakeRefCounted<Sequence>(traits, nullptr, TaskSourceExecutionMode::kParallel)); } @@ -321,6 +299,10 @@ size_t ThreadPoolImpl::GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated( void ThreadPoolImpl::Shutdown() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Cancels an internal service thread task. This must be done before stopping + // the service thread. + delayed_task_manager_.Shutdown(); + // Stop() the ServiceThread before triggering shutdown. This ensures that no // more delayed tasks or file descriptor watches will trigger during shutdown // (preventing http://crbug.com/698140). None of these asynchronous tasks @@ -339,6 +321,8 @@ void ThreadPoolImpl::Shutdown() { // Ensures that there are enough background worker to run BLOCK_SHUTDOWN // tasks. foreground_thread_group_->OnShutdownStarted(); + if (utility_thread_group_) + utility_thread_group_->OnShutdownStarted(); if (background_thread_group_) background_thread_group_->OnShutdownStarted(); @@ -357,6 +341,9 @@ void ThreadPoolImpl::JoinForTesting() { #if DCHECK_IS_ON() DCHECK(!join_for_testing_returned_.IsSet()); #endif + // Cancels an internal service thread task. This must be done before stopping + // the service thread. + delayed_task_manager_.Shutdown(); // The service thread must be stopped before the workers are joined, otherwise // tasks scheduled by the DelayedTaskManager might be posted between joining // those workers and stopping the service thread which will cause a CHECK. See @@ -364,6 +351,8 @@ void ThreadPoolImpl::JoinForTesting() { service_thread_.Stop(); single_thread_task_runner_manager_.JoinForTesting(); foreground_thread_group_->JoinForTesting(); + if (utility_thread_group_) + utility_thread_group_->JoinForTesting(); // IN-TEST if (background_thread_group_) background_thread_group_->JoinForTesting(); #if DCHECK_IS_ON() @@ -400,7 +389,7 @@ void ThreadPoolImpl::EndBestEffortFence() { bool ThreadPoolImpl::PostTaskWithSequenceNow(Task task, scoped_refptr<Sequence> sequence) { auto transaction = sequence->BeginTransaction(); - const bool sequence_should_be_queued = transaction.ShouldBeQueued(); + const bool sequence_should_be_queued = transaction.WillPushImmediateTask(); RegisteredTaskSource task_source; if (sequence_should_be_queued) { task_source = task_tracker_->RegisterTaskSource(sequence); @@ -546,6 +535,12 @@ ThreadGroup* ThreadPoolImpl::GetThreadGroupForTraits(const TaskTraits& traits) { return background_thread_group_.get(); } + if (traits.priority() <= TaskPriority::USER_VISIBLE && + traits.thread_policy() == ThreadPolicy::PREFER_BACKGROUND && + utility_thread_group_) { + return utility_thread_group_.get(); + } + return foreground_thread_group_.get(); } @@ -566,6 +561,8 @@ void ThreadPoolImpl::UpdateCanRunPolicy() { task_tracker_->SetCanRunPolicy(can_run_policy); foreground_thread_group_->DidUpdateCanRunPolicy(); + if (utility_thread_group_) + utility_thread_group_->DidUpdateCanRunPolicy(); if (background_thread_group_) background_thread_group_->DidUpdateCanRunPolicy(); single_thread_task_runner_manager_.DidUpdateCanRunPolicy(); diff --git a/chromium/base/task/thread_pool/thread_pool_impl.h b/chromium/base/task/thread_pool/thread_pool_impl.h index 97730e11b79..b1a21edcdef 100644 --- a/chromium/base/task/thread_pool/thread_pool_impl.h +++ b/chromium/base/task/thread_pool/thread_pool_impl.h @@ -141,12 +141,14 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance, scoped_refptr<Sequence> sequence) override; bool ShouldYield(const TaskSource* task_source) override; + const std::string histogram_label_; const std::unique_ptr<TaskTrackerImpl> task_tracker_; ServiceThread service_thread_; DelayedTaskManager delayed_task_manager_; PooledSingleThreadTaskRunnerManager single_thread_task_runner_manager_; std::unique_ptr<ThreadGroup> foreground_thread_group_; + std::unique_ptr<ThreadGroup> utility_thread_group_; std::unique_ptr<ThreadGroup> background_thread_group_; // Whether this TaskScheduler was started. diff --git a/chromium/base/task/thread_pool/thread_pool_impl_unittest.cc b/chromium/base/task/thread_pool/thread_pool_impl_unittest.cc index 3ab7d3cc298..0f882b3513b 100644 --- a/chromium/base/task/thread_pool/thread_pool_impl_unittest.cc +++ b/chromium/base/task/thread_pool/thread_pool_impl_unittest.cc @@ -64,6 +64,7 @@ namespace internal { namespace { constexpr size_t kMaxNumForegroundThreads = 4; +constexpr size_t kMaxNumUtilityThreads = 2; struct TraitsExecutionModePair { TraitsExecutionModePair(const TaskTraits& traits, @@ -74,11 +75,6 @@ struct TraitsExecutionModePair { TaskSourceExecutionMode execution_mode; }; -struct GroupTypes { - test::GroupType foreground_type; - test::GroupType background_type; -}; - // Returns true if a task with |traits| could run at background thread priority // on this platform. Even if this returns true, it is possible that the task // won't run at background thread priority if a native thread group is used. @@ -88,37 +84,34 @@ bool TraitsSupportBackgroundThreadType(const TaskTraits& traits) { CanUseBackgroundThreadTypeForWorkerThread(); } -#if HAS_NATIVE_THREAD_POOL() -// Returns true if a non-single-threaded task with |traits| is expected to run -// in a native thread group. -bool ShouldRunInNativeThreadGroup(const TaskTraits& traits, - GroupTypes group_types) { - if (traits.priority() == TaskPriority::BEST_EFFORT && - traits.thread_policy() == ThreadPolicy::PREFER_BACKGROUND) { - return group_types.background_type == test::GroupType::NATIVE; - } - return group_types.foreground_type == test::GroupType::NATIVE; +// Returns true if a task with |traits| could run at utility thread +// type on this platform. Even if this returns true, it is possible that the +// task won't run at efficient thread priority if a native thread group is used +// or the utility thread group is disabled. +bool TraitsSupportUtilityThreadType(const TaskTraits& traits) { + return traits.priority() <= TaskPriority::USER_VISIBLE && + traits.thread_policy() == ThreadPolicy::PREFER_BACKGROUND && + CanUseUtilityThreadTypeForWorkerThread(); } -#endif // Verify that the current thread type and I/O restrictions are appropriate to // run a Task with |traits|. // Note: ExecutionMode is verified inside TestTaskFactory. -void VerifyTaskEnvironment(const TaskTraits& traits, GroupTypes group_types) { +void VerifyTaskEnvironment(const TaskTraits& traits, + bool use_resource_efficient_group) { const std::string thread_name(PlatformThread::GetName()); const bool is_single_threaded = (thread_name.find("SingleThread") != std::string::npos); const bool expect_background_thread_type = - TraitsSupportBackgroundThreadType(traits) -#if HAS_NATIVE_THREAD_POOL() - // Native thread groups don't use background thread type. - && (group_types.background_type == test::GroupType::GENERIC || - is_single_threaded) -#endif - ; + TraitsSupportBackgroundThreadType(traits); + + const bool expect_utility_thread_type = + !TraitsSupportBackgroundThreadType(traits) && + TraitsSupportUtilityThreadType(traits) && use_resource_efficient_group; EXPECT_EQ(expect_background_thread_type ? ThreadType::kBackground + : expect_utility_thread_type ? ThreadType::kUtility : ThreadType::kDefault, PlatformThread::GetCurrentThreadType()); @@ -127,18 +120,13 @@ void VerifyTaskEnvironment(const TaskTraits& traits, GroupTypes group_types) { else internal::AssertBlockingDisallowedForTesting(); -#if HAS_NATIVE_THREAD_POOL() - // Native thread groups do not provide the ability to name threads. - if (!is_single_threaded && ShouldRunInNativeThreadGroup(traits, group_types)) - return; -#endif - // Verify that the thread the task is running on is named as expected. EXPECT_THAT(thread_name, ::testing::HasSubstr("ThreadPool")); - EXPECT_THAT(thread_name, ::testing::HasSubstr(expect_background_thread_type - ? "Background" - : "Foreground")); + EXPECT_THAT(thread_name, ::testing::HasSubstr( + expect_background_thread_type ? "Background" + : expect_utility_thread_type ? "Utility" + : "Foreground")); if (is_single_threaded) { // SingleThread workers discriminate blocking/non-blocking tasks. @@ -154,32 +142,33 @@ void VerifyTaskEnvironment(const TaskTraits& traits, GroupTypes group_types) { } void VerifyTaskEnvironmentAndSignalEvent(const TaskTraits& traits, - GroupTypes group_types, + bool use_resource_efficient_group, TestWaitableEvent* event) { DCHECK(event); - VerifyTaskEnvironment(traits, group_types); + VerifyTaskEnvironment(traits, use_resource_efficient_group); event->Signal(); } -void VerifyTimeAndTaskEnvironmentAndSignalEvent(const TaskTraits& traits, - GroupTypes group_types, - TimeTicks expected_time, - TestWaitableEvent* event) { +void VerifyTimeAndTaskEnvironmentAndSignalEvent( + const TaskTraits& traits, + bool use_resource_efficient_group, + TimeTicks expected_time, + TestWaitableEvent* event) { DCHECK(event); EXPECT_LE(expected_time, TimeTicks::Now()); - VerifyTaskEnvironment(traits, group_types); + VerifyTaskEnvironment(traits, use_resource_efficient_group); event->Signal(); } void VerifyOrderAndTaskEnvironmentAndSignalEvent( const TaskTraits& traits, - GroupTypes group_types, + bool use_resource_efficient_group, TestWaitableEvent* expected_previous_event, TestWaitableEvent* event) { DCHECK(event); if (expected_previous_event) EXPECT_TRUE(expected_previous_event->IsSignaled()); - VerifyTaskEnvironment(traits, group_types); + VerifyTaskEnvironment(traits, use_resource_efficient_group); event->Signal(); } @@ -211,11 +200,11 @@ class ThreadPostingTasks : public SimpleThread { // |execution_mode|. ThreadPostingTasks(ThreadPoolImpl* thread_pool, const TaskTraits& traits, - GroupTypes group_types, + bool use_resource_efficient_group, TaskSourceExecutionMode execution_mode) : SimpleThread("ThreadPostingTasks"), traits_(traits), - group_types_(group_types), + use_resource_efficient_group_(use_resource_efficient_group), factory_(CreateTaskRunnerAndExecutionMode(thread_pool, traits, execution_mode), @@ -230,14 +219,14 @@ class ThreadPostingTasks : public SimpleThread { void Run() override { const size_t kNumTasksPerThread = 150; for (size_t i = 0; i < kNumTasksPerThread; ++i) { - factory_.PostTask( - test::TestTaskFactory::PostNestedTask::NO, - BindOnce(&VerifyTaskEnvironment, traits_, group_types_)); + factory_.PostTask(test::TestTaskFactory::PostNestedTask::NO, + BindOnce(&VerifyTaskEnvironment, traits_, + use_resource_efficient_group_)); } } const TaskTraits traits_; - GroupTypes group_types_; + bool use_resource_efficient_group_; test::TestTaskFactory factory_; }; @@ -276,10 +265,14 @@ std::vector<TraitsExecutionModePair> GetTraitsExecutionModePairsToCoverAllSchedulingOptions() { return {TraitsExecutionModePair({TaskPriority::BEST_EFFORT}, TaskSourceExecutionMode::kSequenced), + TraitsExecutionModePair({TaskPriority::USER_VISIBLE}, + TaskSourceExecutionMode::kSequenced), TraitsExecutionModePair({TaskPriority::USER_BLOCKING}, TaskSourceExecutionMode::kSequenced), TraitsExecutionModePair({TaskPriority::BEST_EFFORT}, TaskSourceExecutionMode::kSingleThread), + TraitsExecutionModePair({TaskPriority::USER_VISIBLE}, + TaskSourceExecutionMode::kSingleThread), TraitsExecutionModePair({TaskPriority::USER_BLOCKING}, TaskSourceExecutionMode::kSingleThread)}; } @@ -297,6 +290,8 @@ class ThreadPoolImplTestBase : public testing::Test { ThreadPoolImplTestBase(const ThreadPoolImplTestBase&) = delete; ThreadPoolImplTestBase& operator=(const ThreadPoolImplTestBase&) = delete; + virtual bool GetUseResourceEfficientThreadGroup() const = 0; + void set_worker_thread_observer( WorkerThreadObserver* worker_thread_observer) { worker_thread_observer_ = worker_thread_observer; @@ -304,10 +299,12 @@ class ThreadPoolImplTestBase : public testing::Test { void StartThreadPool( size_t max_num_foreground_threads = kMaxNumForegroundThreads, + size_t max_num_utility_threads = kMaxNumUtilityThreads, TimeDelta reclaim_time = Seconds(30)) { SetupFeatures(); - ThreadPoolInstance::InitParams init_params(max_num_foreground_threads); + ThreadPoolInstance::InitParams init_params(max_num_foreground_threads, + max_num_utility_threads); init_params.suggested_reclaim_time = reclaim_time; thread_pool_->Start(init_params, worker_thread_observer_); @@ -324,8 +321,6 @@ class ThreadPoolImplTestBase : public testing::Test { did_tear_down_ = true; } - virtual GroupTypes GetGroupTypes() const = 0; - std::unique_ptr<ThreadPoolImpl> thread_pool_; Thread service_thread_; @@ -333,12 +328,8 @@ class ThreadPoolImplTestBase : public testing::Test { void SetupFeatures() { std::vector<base::test::FeatureRef> features; -#if HAS_NATIVE_THREAD_POOL() - if (GetGroupTypes().foreground_type == test::GroupType::NATIVE) - features.push_back(kUseNativeThreadPool); - if (GetGroupTypes().background_type == test::GroupType::NATIVE) - features.push_back(kUseBackgroundNativeThreadPool); -#endif + if (GetUseResourceEfficientThreadGroup()) + features.push_back(kUseUtilityThreadGroup); if (!features.empty()) feature_list_.InitWithFeatures(features, {}); @@ -350,13 +341,12 @@ class ThreadPoolImplTestBase : public testing::Test { }; class ThreadPoolImplTest : public ThreadPoolImplTestBase, - public testing::WithParamInterface<GroupTypes> { + public testing::WithParamInterface< + bool /* use_resource_efficient_thread_group */> { public: - ThreadPoolImplTest() = default; - ThreadPoolImplTest(const ThreadPoolImplTest&) = delete; - ThreadPoolImplTest& operator=(const ThreadPoolImplTest&) = delete; - - GroupTypes GetGroupTypes() const override { return GetParam(); } + bool GetUseResourceEfficientThreadGroup() const override { + return GetParam(); + } }; // Tests run for enough traits and execution mode combinations to cover all @@ -365,7 +355,8 @@ class ThreadPoolImplTest : public ThreadPoolImplTestBase, class ThreadPoolImplTest_CoverAllSchedulingOptions : public ThreadPoolImplTestBase, public testing::WithParamInterface< - std::tuple<GroupTypes, TraitsExecutionModePair>> { + std::tuple<bool /* use_resource_efficient_thread_group */, + TraitsExecutionModePair>> { public: ThreadPoolImplTest_CoverAllSchedulingOptions() = default; ThreadPoolImplTest_CoverAllSchedulingOptions( @@ -373,7 +364,9 @@ class ThreadPoolImplTest_CoverAllSchedulingOptions ThreadPoolImplTest_CoverAllSchedulingOptions& operator=( const ThreadPoolImplTest_CoverAllSchedulingOptions&) = delete; - GroupTypes GetGroupTypes() const override { return std::get<0>(GetParam()); } + bool GetUseResourceEfficientThreadGroup() const override { + return std::get<0>(GetParam()); + } TaskTraits GetTraits() const { return std::get<1>(GetParam()).traits; } TaskSourceExecutionMode GetExecutionMode() const { return std::get<1>(GetParam()).execution_mode; @@ -391,7 +384,7 @@ TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostDelayedTaskNoDelay) { thread_pool_->PostDelayedTask( FROM_HERE, GetTraits(), BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetTraits(), - GetGroupTypes(), Unretained(&task_ran)), + GetUseResourceEfficientThreadGroup(), Unretained(&task_ran)), TimeDelta()); task_ran.Wait(); } @@ -406,7 +399,8 @@ TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostDelayedTaskWithDelay) { thread_pool_->PostDelayedTask( FROM_HERE, GetTraits(), BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetTraits(), - GetGroupTypes(), TimeTicks::Now() + TestTimeouts::tiny_timeout(), + GetUseResourceEfficientThreadGroup(), + TimeTicks::Now() + TestTimeouts::tiny_timeout(), Unretained(&task_ran)), TestTimeouts::tiny_timeout()); task_ran.Wait(); @@ -449,7 +443,7 @@ TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, ->PostCancelableDelayedTaskAt( subtle::PostDelayedTaskPassKeyForTesting(), FROM_HERE, BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetTraits(), - GetGroupTypes(), + GetUseResourceEfficientThreadGroup(), TimeTicks::Now() + TestTimeouts::tiny_timeout(), Unretained(&task_ran)), TimeTicks::Now() + TestTimeouts::tiny_timeout(), @@ -469,9 +463,9 @@ TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostTasksViaTaskRunner) { const size_t kNumTasksPerTest = 150; for (size_t i = 0; i < kNumTasksPerTest; ++i) { - factory.PostTask( - test::TestTaskFactory::PostNestedTask::NO, - BindOnce(&VerifyTaskEnvironment, GetTraits(), GetGroupTypes())); + factory.PostTask(test::TestTaskFactory::PostNestedTask::NO, + BindOnce(&VerifyTaskEnvironment, GetTraits(), + GetUseResourceEfficientThreadGroup())); } factory.WaitForAllTasksToRun(); @@ -485,7 +479,7 @@ TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, thread_pool_->PostDelayedTask( FROM_HERE, GetTraits(), BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetTraits(), - GetGroupTypes(), Unretained(&task_running)), + GetUseResourceEfficientThreadGroup(), Unretained(&task_running)), TimeDelta()); // Wait a little bit to make sure that the task doesn't run before Start(). @@ -507,7 +501,8 @@ TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, thread_pool_->PostDelayedTask( FROM_HERE, GetTraits(), BindOnce(&VerifyTimeAndTaskEnvironmentAndSignalEvent, GetTraits(), - GetGroupTypes(), TimeTicks::Now() + TestTimeouts::tiny_timeout(), + GetUseResourceEfficientThreadGroup(), + TimeTicks::Now() + TestTimeouts::tiny_timeout(), Unretained(&task_running)), TestTimeouts::tiny_timeout()); @@ -526,12 +521,20 @@ TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, // called. TEST_P(ThreadPoolImplTest_CoverAllSchedulingOptions, PostTaskViaTaskRunnerBeforeStart) { + bool use_resource_efficient_thread_group = + GetUseResourceEfficientThreadGroup(); + // The worker_thread of SingleThreadTaskRunner is selected before + // kUseUtilityThreadGroup feature is set up at StartThreadPool(). + if (GetExecutionMode() == TaskSourceExecutionMode::kSingleThread) { + use_resource_efficient_thread_group = false; + } TestWaitableEvent task_running; CreateTaskRunnerAndExecutionMode(thread_pool_.get(), GetTraits(), GetExecutionMode()) ->PostTask(FROM_HERE, BindOnce(&VerifyTaskEnvironmentAndSignalEvent, GetTraits(), - GetGroupTypes(), Unretained(&task_running))); + use_resource_efficient_thread_group, + Unretained(&task_running))); // Wait a little bit to make sure that the task doesn't run before Start(). // Note: This test won't catch a case where the task runs just after the check @@ -595,7 +598,8 @@ TEST(ThreadPoolImplTest_Switch, DisableBestEffortTasksSwitch) { switches::kDisableBestEffortTasks); ThreadPoolImpl thread_pool("Test"); - ThreadPoolInstance::InitParams init_params(kMaxNumForegroundThreads); + ThreadPoolInstance::InitParams init_params(kMaxNumForegroundThreads, + kMaxNumUtilityThreads); thread_pool.Start(init_params, nullptr); AtomicFlag best_effort_can_run; @@ -790,8 +794,8 @@ TEST_P(ThreadPoolImplTest, MultipleTraitsExecutionModePair) { std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks; for (const auto& test_params : GetTraitsExecutionModePairs()) { threads_posting_tasks.push_back(std::make_unique<ThreadPostingTasks>( - thread_pool_.get(), test_params.traits, GetGroupTypes(), - test_params.execution_mode)); + thread_pool_.get(), test_params.traits, + GetUseResourceEfficientThreadGroup(), test_params.execution_mode)); threads_posting_tasks.back()->Start(); } @@ -817,21 +821,22 @@ TEST_P(ThreadPoolImplTest, {MayBlock(), TaskPriority::BEST_EFFORT}); }); - const size_t expected_max = - GetGroupTypes().foreground_type == test::GroupType::GENERIC - ? kMaxNumForegroundThreads - : static_cast<size_t>(std::max(3, SysInfo::NumberOfProcessors() - 1)); - - EXPECT_EQ(expected_max, + EXPECT_EQ(GetUseResourceEfficientThreadGroup() && + CanUseUtilityThreadTypeForWorkerThread() + ? kMaxNumUtilityThreads + : kMaxNumForegroundThreads, thread_pool_->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated( {TaskPriority::USER_VISIBLE})); - EXPECT_EQ(expected_max, + EXPECT_EQ(GetUseResourceEfficientThreadGroup() && + CanUseUtilityThreadTypeForWorkerThread() + ? kMaxNumUtilityThreads + : kMaxNumForegroundThreads, thread_pool_->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated( {MayBlock(), TaskPriority::USER_VISIBLE})); - EXPECT_EQ(expected_max, + EXPECT_EQ(kMaxNumForegroundThreads, thread_pool_->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated( {TaskPriority::USER_BLOCKING})); - EXPECT_EQ(expected_max, + EXPECT_EQ(kMaxNumForegroundThreads, thread_pool_->GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated( {MayBlock(), TaskPriority::USER_BLOCKING})); } @@ -1150,22 +1155,32 @@ TEST_P(ThreadPoolImplTest, WorkerThreadObserver) { testing::StrictMock<test::MockWorkerThreadObserver> observer; set_worker_thread_observer(&observer); - // A worker should be created for each generic thread group. After that, 4 - // threads should be created for each SingleThreadTaskRunnerThreadMode (8 on - // Windows). WorkerThreads are not created (and hence not observed) when using - // the native thread pools. - const int kExpectedNumForegroundPoolWorkers = - (GetGroupTypes().foreground_type == test::GroupType::GENERIC) ? 1 : 0; - const int kExpectedNumBackgroundPoolWorkers = - (GetGroupTypes().background_type == test::GroupType::GENERIC && - CanUseBackgroundThreadTypeForWorkerThread()) + // A worker should be created for each thread group. After that, 4 threads + // should be created for each SingleThreadTaskRunnerThreadMode (8 on Windows). + const int kExpectedNumForegroundPoolWorkers = 1; + const int kExpectedNumUtilityPoolWorkers = + GetUseResourceEfficientThreadGroup() && + CanUseUtilityThreadTypeForWorkerThread() ? 1 : 0; - const int kExpectedNumPoolWorkers = - kExpectedNumForegroundPoolWorkers + kExpectedNumBackgroundPoolWorkers; + const int kExpectedNumBackgroundPoolWorkers = + CanUseBackgroundThreadTypeForWorkerThread() ? 1 : 0; + const int kExpectedNumPoolWorkers = kExpectedNumForegroundPoolWorkers + + kExpectedNumUtilityPoolWorkers + + kExpectedNumBackgroundPoolWorkers; + const int kExpectedNumSharedSingleThreadedForegroundWorkers = 2; + const int kExpectedNumSharedSingleThreadedUtilityWorkers = + GetUseResourceEfficientThreadGroup() && + CanUseUtilityThreadTypeForWorkerThread() + ? 2 + : 0; + const int kExpectedNumSharedSingleThreadedBackgroundWorkers = + CanUseBackgroundThreadTypeForWorkerThread() ? 2 : 0; const int kExpectedNumSharedSingleThreadedWorkers = - CanUseBackgroundThreadTypeForWorkerThread() ? 4 : 2; - const int kExpectedNumDedicatedSingleThreadedWorkers = 4; + kExpectedNumSharedSingleThreadedForegroundWorkers + + kExpectedNumSharedSingleThreadedUtilityWorkers + + kExpectedNumSharedSingleThreadedBackgroundWorkers; + const int kExpectedNumDedicatedSingleThreadedWorkers = 6; const int kExpectedNumCOMSharedSingleThreadedWorkers = #if BUILDFLAG(IS_WIN) @@ -1188,7 +1203,8 @@ TEST_P(ThreadPoolImplTest, WorkerThreadObserver) { // Infinite detach time to prevent workers from invoking // OnWorkerThreadMainExit() earlier than expected. - StartThreadPool(kMaxNumForegroundThreads, TimeDelta::Max()); + StartThreadPool(kMaxNumForegroundThreads, kMaxNumUtilityThreads, + TimeDelta::Max()); std::vector<scoped_refptr<SingleThreadTaskRunner>> task_runners; @@ -1198,6 +1214,11 @@ TEST_P(ThreadPoolImplTest, WorkerThreadObserver) { {TaskPriority::BEST_EFFORT, MayBlock()}, SingleThreadTaskRunnerThreadMode::SHARED)); task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner( + {TaskPriority::USER_VISIBLE}, SingleThreadTaskRunnerThreadMode::SHARED)); + task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner( + {TaskPriority::USER_VISIBLE, MayBlock()}, + SingleThreadTaskRunnerThreadMode::SHARED)); + task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner( {TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::SHARED)); task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner( {TaskPriority::USER_BLOCKING, MayBlock()}, @@ -1210,6 +1231,12 @@ TEST_P(ThreadPoolImplTest, WorkerThreadObserver) { {TaskPriority::BEST_EFFORT, MayBlock()}, SingleThreadTaskRunnerThreadMode::DEDICATED)); task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner( + {TaskPriority::USER_VISIBLE}, + SingleThreadTaskRunnerThreadMode::DEDICATED)); + task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner( + {TaskPriority::USER_VISIBLE, MayBlock()}, + SingleThreadTaskRunnerThreadMode::DEDICATED)); + task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner( {TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::DEDICATED)); task_runners.push_back(thread_pool_->CreateSingleThreadTaskRunner( @@ -1223,6 +1250,11 @@ TEST_P(ThreadPoolImplTest, WorkerThreadObserver) { {TaskPriority::BEST_EFFORT, MayBlock()}, SingleThreadTaskRunnerThreadMode::SHARED)); task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner( + {TaskPriority::USER_VISIBLE}, SingleThreadTaskRunnerThreadMode::SHARED)); + task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner( + {TaskPriority::USER_VISIBLE, MayBlock()}, + SingleThreadTaskRunnerThreadMode::SHARED)); + task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner( {TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::SHARED)); task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner( {TaskPriority::USER_BLOCKING, MayBlock()}, @@ -1235,6 +1267,12 @@ TEST_P(ThreadPoolImplTest, WorkerThreadObserver) { {TaskPriority::BEST_EFFORT, MayBlock()}, SingleThreadTaskRunnerThreadMode::DEDICATED)); task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner( + {TaskPriority::USER_VISIBLE}, + SingleThreadTaskRunnerThreadMode::DEDICATED)); + task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner( + {TaskPriority::USER_VISIBLE, MayBlock()}, + SingleThreadTaskRunnerThreadMode::DEDICATED)); + task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner( {TaskPriority::USER_BLOCKING}, SingleThreadTaskRunnerThreadMode::DEDICATED)); task_runners.push_back(thread_pool_->CreateCOMSTATaskRunner( @@ -1287,17 +1325,17 @@ TEST_P(ThreadPoolImplTest, ThreadGroupChangeShouldYield) { TestWaitableEvent threads_continue; auto job_task = base::MakeRefCounted<test::MockJobTask>( - BindLambdaForTesting([&threads_running, - &threads_continue](JobDelegate* delegate) { - EXPECT_FALSE(delegate->ShouldYield()); + BindLambdaForTesting( + [&threads_running, &threads_continue](JobDelegate* delegate) { + EXPECT_FALSE(delegate->ShouldYield()); - threads_running.Signal(); - threads_continue.Wait(); + threads_running.Signal(); + threads_continue.Wait(); - // The task source needs to yield if background thread groups exist. - EXPECT_EQ(delegate->ShouldYield(), - CanUseBackgroundThreadTypeForWorkerThread()); - }), + // The task source needs to yield if background thread groups exist. + EXPECT_EQ(delegate->ShouldYield(), + CanUseBackgroundThreadTypeForWorkerThread()); + }), /* num_tasks_to_run */ 1); scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource( FROM_HERE, {TaskPriority::USER_VISIBLE}, thread_pool_.get()); @@ -1388,8 +1426,9 @@ struct TaskRunnerAndEvents { // Create a series of sample task runners that will post tasks at various // initial priorities, then update priority. std::vector<std::unique_ptr<TaskRunnerAndEvents>> CreateTaskRunnersAndEvents( - ThreadPoolImpl* thread_pool, + ThreadPoolImplTest* test, ThreadPolicy thread_policy) { + ThreadPoolImpl* thread_pool = test->thread_pool_.get(); std::vector<std::unique_ptr<TaskRunnerAndEvents>> task_runners_and_events; // ----- @@ -1402,11 +1441,17 @@ std::vector<std::unique_ptr<TaskRunnerAndEvents>> CreateTaskRunnersAndEvents( // ----- // Task runner that will start as BEST_EFFORT and update to USER_VISIBLE. - // Its task is expected to run after the USER_BLOCKING task runner's task. + // Its task is expected to run after the USER_BLOCKING task runner's task, + // unless resource-efficient thread group exists, in which case they will run + // asynchronously. + TestWaitableEvent* expected_previous_event = + test->GetUseResourceEfficientThreadGroup() + ? nullptr + : &task_runners_and_events.back()->task_ran; task_runners_and_events.push_back(std::make_unique<TaskRunnerAndEvents>( thread_pool->CreateUpdateableSequencedTaskRunner( {TaskPriority::BEST_EFFORT, thread_policy}), - TaskPriority::USER_VISIBLE, &task_runners_and_events.back()->task_ran)); + TaskPriority::USER_VISIBLE, expected_previous_event)); // ----- // Task runner that will start as USER_BLOCKING and update to BEST_EFFORT. Its @@ -1417,10 +1462,9 @@ std::vector<std::unique_ptr<TaskRunnerAndEvents>> CreateTaskRunnersAndEvents( // If the task following the priority update is expected to run in the // foreground group, it should be after the task posted to the TaskRunner // whose priority is updated to USER_VISIBLE. - TestWaitableEvent* expected_previous_event = - CanUseBackgroundThreadTypeForWorkerThread() - ? nullptr - : &task_runners_and_events.back()->task_ran; + expected_previous_event = CanUseBackgroundThreadTypeForWorkerThread() + ? nullptr + : &task_runners_and_events.back()->task_ran; task_runners_and_events.push_back(std::make_unique<TaskRunnerAndEvents>( thread_pool->CreateUpdateableSequencedTaskRunner( @@ -1441,7 +1485,7 @@ void TestUpdatePrioritySequenceNotScheduled(ThreadPoolImplTest* test, test->StartThreadPool(kLocalMaxNumForegroundThreads); auto task_runners_and_events = - CreateTaskRunnersAndEvents(test->thread_pool_.get(), thread_policy); + CreateTaskRunnersAndEvents(test, thread_policy); // Prevent tasks from running. test->thread_pool_->BeginFence(); @@ -1449,35 +1493,13 @@ void TestUpdatePrioritySequenceNotScheduled(ThreadPoolImplTest* test, // Post tasks to multiple task runners while they are at initial priority. // They won't run immediately because of the call to BeginFence() above. for (auto& task_runner_and_events : task_runners_and_events) { -#if HAS_NATIVE_THREAD_POOL() - const bool runs_in_background_group = - (task_runner_and_events->updated_priority == - TaskPriority::BEST_EFFORT && - thread_policy == ThreadPolicy::PREFER_BACKGROUND && - CanUseBackgroundThreadTypeForWorkerThread()); -#endif - task_runner_and_events->task_runner->PostTask( FROM_HERE, BindOnce( &VerifyOrderAndTaskEnvironmentAndSignalEvent, TaskTraits{task_runner_and_events->updated_priority, thread_policy}, - test->GetGroupTypes(), - // Native pools ignore the maximum number of threads per pool - // and therefore don't guarantee that tasks run in priority - // order (see comment at beginning of test). - Unretained( -#if HAS_NATIVE_THREAD_POOL() - ((runs_in_background_group && - test->GetGroupTypes().background_type == - test::GroupType::NATIVE) || - (!runs_in_background_group && - test->GetGroupTypes().foreground_type == - test::GroupType::NATIVE)) - ? nullptr - : -#endif - task_runner_and_events->expected_previous_event.get()), + test->GetUseResourceEfficientThreadGroup(), + Unretained(task_runner_and_events->expected_previous_event.get()), Unretained(&task_runner_and_events->task_ran))); } @@ -1500,7 +1522,7 @@ void TestUpdatePrioritySequenceScheduled(ThreadPoolImplTest* test, ThreadPolicy thread_policy) { test->StartThreadPool(); auto task_runners_and_events = - CreateTaskRunnersAndEvents(test->thread_pool_.get(), thread_policy); + CreateTaskRunnersAndEvents(test, thread_policy); // Post blocking tasks to all task runners to prevent tasks from being // scheduled later in the test. @@ -1528,7 +1550,7 @@ void TestUpdatePrioritySequenceScheduled(ThreadPoolImplTest* test, BindOnce( &VerifyOrderAndTaskEnvironmentAndSignalEvent, TaskTraits{task_runner_and_events->updated_priority, thread_policy}, - test->GetGroupTypes(), + test->GetUseResourceEfficientThreadGroup(), Unretained(task_runner_and_events->expected_previous_event), Unretained(&task_runner_and_events->task_ran))); } @@ -1582,25 +1604,13 @@ TEST_P(ThreadPoolImplTest, UpdatePriorityFromBestEffortNoThreadPolicy) { } } -auto GetGroupTypes() { - return ::testing::Values( - GroupTypes { test::GroupType::GENERIC, test::GroupType::GENERIC } -#if HAS_NATIVE_THREAD_POOL() - , - GroupTypes{test::GroupType::NATIVE, test::GroupType::GENERIC}, - // To keep running time low, we don't test (GENERIC, NATIVE). - GroupTypes { test::GroupType::NATIVE, test::GroupType::NATIVE } -#endif - ); -} - -INSTANTIATE_TEST_SUITE_P(All, ThreadPoolImplTest, GetGroupTypes()); +INSTANTIATE_TEST_SUITE_P(All, ThreadPoolImplTest, ::testing::Bool()); INSTANTIATE_TEST_SUITE_P( All, ThreadPoolImplTest_CoverAllSchedulingOptions, ::testing::Combine( - GetGroupTypes(), + ::testing::Bool(), ::testing::ValuesIn( GetTraitsExecutionModePairsToCoverAllSchedulingOptions()))); diff --git a/chromium/base/task/thread_pool/thread_pool_instance.cc b/chromium/base/task/thread_pool/thread_pool_instance.cc index be19c0c832c..d5173c7afb9 100644 --- a/chromium/base/task/thread_pool/thread_pool_instance.cc +++ b/chromium/base/task/thread_pool/thread_pool_instance.cc @@ -21,10 +21,26 @@ namespace { // |g_thread_pool| is intentionally leaked on shutdown. ThreadPoolInstance* g_thread_pool = nullptr; +size_t GetDefaultMaxNumUtilityThreads(size_t max_num_foreground_threads_in) { + int num_of_efficient_processors = SysInfo::NumberOfEfficientProcessors(); + if (num_of_efficient_processors != 0) { + DCHECK_GT(num_of_efficient_processors, 0); + return static_cast<size_t>(num_of_efficient_processors); + } + return std::max<size_t>(1, max_num_foreground_threads_in / 2); +} + } // namespace ThreadPoolInstance::InitParams::InitParams(size_t max_num_foreground_threads_in) - : max_num_foreground_threads(max_num_foreground_threads_in) {} + : max_num_foreground_threads(max_num_foreground_threads_in), + max_num_utility_threads( + GetDefaultMaxNumUtilityThreads(max_num_foreground_threads_in)) {} + +ThreadPoolInstance::InitParams::InitParams(size_t max_num_foreground_threads_in, + size_t max_num_utility_threads_in) + : max_num_foreground_threads(max_num_foreground_threads_in), + max_num_utility_threads(max_num_utility_threads_in) {} ThreadPoolInstance::InitParams::~InitParams() = default; diff --git a/chromium/base/task/thread_pool/thread_pool_instance.h b/chromium/base/task/thread_pool/thread_pool_instance.h index a8508cf2c42..4f0214661a0 100644 --- a/chromium/base/task/thread_pool/thread_pool_instance.h +++ b/chromium/base/task/thread_pool/thread_pool_instance.h @@ -58,12 +58,18 @@ class BASE_EXPORT ThreadPoolInstance { }; InitParams(size_t max_num_foreground_threads_in); + InitParams(size_t max_num_foreground_threads_in, + size_t max_num_utility_threads_in); ~InitParams(); // Maximum number of unblocked tasks that can run concurrently in the // foreground thread group. size_t max_num_foreground_threads; + // Maximum number of unblocked tasks that can run concurrently in the + // utility thread group. + size_t max_num_utility_threads; + // Whether COM is initialized when running sequenced and parallel tasks. CommonThreadPoolEnvironment common_thread_pool_environment = CommonThreadPoolEnvironment::DEFAULT; diff --git a/chromium/base/task/thread_pool/worker_thread.cc b/chromium/base/task/thread_pool/worker_thread.cc index ec83b3c38f1..f5807b06a2d 100644 --- a/chromium/base/task/thread_pool/worker_thread.cc +++ b/chromium/base/task/thread_pool/worker_thread.cc @@ -18,6 +18,7 @@ #include "base/debug/alias.h" #include "base/feature_list.h" #include "base/synchronization/waitable_event.h" +#include "base/task/task_features.h" #include "base/task/thread_pool/environment_config.h" #include "base/task/thread_pool/task_tracker.h" #include "base/task/thread_pool/worker_thread_observer.h" @@ -41,14 +42,17 @@ #include "base/allocator/partition_allocator/thread_cache.h" #endif +namespace base::internal { + namespace { + #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \ defined(PA_THREAD_CACHE_SUPPORTED) // Returns the desired sleep time before the worker has to wake up to purge // the cache thread or reclaim itself. |min_sleep_time| contains the minimal // acceptable amount of time to sleep. -base::TimeDelta GetSleepTimeBeforePurge(base::TimeDelta min_sleep_time) { - const base::TimeTicks now = base::TimeTicks::Now(); +TimeDelta GetSleepTimeBeforePurge(TimeDelta min_sleep_time) { + const TimeTicks now = TimeTicks::Now(); // Do not wake up to purge within the first minute of process lifetime. In // short lived processes this will avoid waking up to try and save memory @@ -56,27 +60,31 @@ base::TimeDelta GetSleepTimeBeforePurge(base::TimeDelta min_sleep_time) { // should allow for better performance at process startup since even if a // worker goes to sleep for kPurgeThreadCacheIdleDelay it's very likely it // will be needed soon after because of heavy startup workloads. - constexpr base::TimeDelta kFirstSleepLength = base::Minutes(1); + constexpr TimeDelta kFirstSleepLength = Minutes(1); // Use the first time a worker goes to sleep in this process as an // approximation of the process creation time. - static const base::TimeTicks first_scheduled_wake = now + kFirstSleepLength; + static const TimeTicks first_scheduled_wake = now + kFirstSleepLength; // Align wakeups for purges to reduce the chances of taking the CPU out of // sleep multiple times for these operations. - constexpr base::TimeDelta kPurgeThreadCacheIdleDelay = base::Seconds(1); - const base::TimeTicks snapped_wake = + constexpr TimeDelta kPurgeThreadCacheIdleDelay = Seconds(1); + const TimeTicks snapped_wake = (now + min_sleep_time) - .SnappedToNextTick(base::TimeTicks(), kPurgeThreadCacheIdleDelay); + .SnappedToNextTick(TimeTicks(), kPurgeThreadCacheIdleDelay); // Avoid scheduling at |first_scheduled_wake| if it would result in a sleep // that's too short. return std::max(snapped_wake - now, first_scheduled_wake - now); } #endif -} // namespace -namespace base::internal { +bool IsDelayFirstWorkerSleepEnabled() { + static bool state = FeatureList::IsEnabled(kDelayFirstWorkerWake); + return state; +} + +} // namespace constexpr TimeDelta WorkerThread::Delegate::kPurgeThreadCacheIdleDelay; @@ -102,16 +110,7 @@ void WorkerThread::Delegate::WaitForWork(WaitableEvent* wake_up_event) { defined(PA_THREAD_CACHE_SUPPORTED) TimeDelta min_sleep_time = std::min(sleep_time, kPurgeThreadCacheIdleDelay); - static BASE_FEATURE(kDelayFirstWorkerWake, "DelayFirstWorkerWake", - base::FEATURE_DISABLED_BY_DEFAULT); - // ThreadPoolInstance::Start() must always be after FeatureList - // initialization. This means this function has access to the feature state on - // first call. Cache the feature check to avoid the overhead of calling - // IsEnabled() every time. - static const bool is_delay_first_worker_sleep_enabled = - FeatureList::IsEnabled(kDelayFirstWorkerWake); - - if (is_delay_first_worker_sleep_enabled) + if (IsDelayFirstWorkerSleepEnabled()) min_sleep_time = GetSleepTimeBeforePurge(min_sleep_time); const bool was_signaled = wake_up_event->TimedWait(min_sleep_time); @@ -137,23 +136,43 @@ void WorkerThread::Delegate::WaitForWork(WaitableEvent* wake_up_event) { WorkerThread::WorkerThread(ThreadType thread_type_hint, std::unique_ptr<Delegate> delegate, TrackedRef<TaskTracker> task_tracker, + size_t sequence_num, const CheckedLock* predecessor_lock) : thread_lock_(predecessor_lock), delegate_(std::move(delegate)), task_tracker_(std::move(task_tracker)), thread_type_hint_(thread_type_hint), - current_thread_type_(GetDesiredThreadType()) { + current_thread_type_(GetDesiredThreadType()), + sequence_num_(sequence_num) { DCHECK(delegate_); DCHECK(task_tracker_); DCHECK(CanUseBackgroundThreadTypeForWorkerThread() || thread_type_hint_ != ThreadType::kBackground); + DCHECK(CanUseUtilityThreadTypeForWorkerThread() || + thread_type_hint != ThreadType::kUtility); wake_up_event_.declare_only_used_while_idle(); + wake_up_event_.opt_out_of_wakeup_flow_events(); } bool WorkerThread::Start( scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner, WorkerThreadObserver* worker_thread_observer) { CheckedLock::AssertNoLockHeldOnCurrentThread(); + + // Prime kDelayFirstWorkerWake's feature state right away on thread creation + // instead of looking it up for the first time later on thread as this avoids + // a data race in tests that may ~FeatureList while the first worker thread + // is still initializing (the first WorkerThread will be started on the main + // thread as part of ThreadPoolImpl::Start() so doing it then avoids this + // race), crbug.com/1344573. + // Note 1: the feature state is always available at this point as + // ThreadPoolInstance::Start() contractually happens-after FeatureList + // initialization. + // Note 2: This is done on Start instead of in the constructor as construction + // happens under a ThreadGroupImpl lock which precludes calling into + // FeatureList (as that can also use a lock). + IsDelayFirstWorkerSleepEnabled(); + CheckedAutoLock auto_lock(thread_lock_); DCHECK(thread_handle_.is_null()); @@ -191,6 +210,8 @@ void WorkerThread::WakeUp() { // WorkerThread cannot run more tasks. DCHECK(!join_called_for_testing_.IsSet()); DCHECK(!should_exit_.IsSet()); + TRACE_EVENT_INSTANT("wakeup.flow", "WorkerThread::WakeUp", + perfetto::Flow::FromPointer(this)); wake_up_event_.Signal(); } @@ -412,9 +433,10 @@ void WorkerThread::RunWorker() { // TODO(crbug.com/1021571): Remove this once fixed. PERFETTO_INTERNAL_ADD_EMPTY_EVENT(); delegate_->WaitForWork(&wake_up_event_); - TRACE_EVENT_BEGIN0("base", "WorkerThread active"); + TRACE_EVENT_BEGIN("base", "WorkerThread active", + perfetto::TerminatingFlow::FromPointer(this)); } - + bool got_work_this_wakeup = false; while (!ShouldExit()) { #if BUILDFLAG(IS_APPLE) mac::ScopedNSAutoreleasePool autorelease_pool; @@ -432,15 +454,25 @@ void WorkerThread::RunWorker() { if (ShouldExit()) break; + // If this is the first time we called GetWork and the worker's still + // alive, record that this is an unnecessary wakeup. + if (!got_work_this_wakeup) + delegate_->RecordUnnecessaryWakeup(); + TRACE_EVENT_END0("base", "WorkerThread active"); // TODO(crbug.com/1021571): Remove this once fixed. PERFETTO_INTERNAL_ADD_EMPTY_EVENT(); hang_watch_scope.reset(); delegate_->WaitForWork(&wake_up_event_); - TRACE_EVENT_BEGIN0("base", "WorkerThread active"); + got_work_this_wakeup = false; + + TRACE_EVENT_BEGIN("base", "WorkerThread active", + perfetto::TerminatingFlow::FromPointer(this)); continue; } + got_work_this_wakeup = true; + // Alias pointer for investigation of memory corruption. crbug.com/1218384 TaskSource* task_source_before_run = task_source.get(); base::debug::Alias(&task_source_before_run); diff --git a/chromium/base/task/thread_pool/worker_thread.h b/chromium/base/task/thread_pool/worker_thread.h index 28104df7a8c..77b2fc1cc94 100644 --- a/chromium/base/task/thread_pool/worker_thread.h +++ b/chromium/base/task/thread_pool/worker_thread.h @@ -93,6 +93,10 @@ class BASE_EXPORT WorkerThread : public RefCountedThreadSafe<WorkerThread>, // TaskTracker after calling OnMainExit() on the Delegate. virtual void OnMainExit(WorkerThread* worker) {} + // Called by a WorkerThread when it is woken up without any work being + // available for it to run. + virtual void RecordUnnecessaryWakeup() {} + static constexpr TimeDelta kPurgeThreadCacheIdleDelay = Seconds(1); }; @@ -101,6 +105,7 @@ class BASE_EXPORT WorkerThread : public RefCountedThreadSafe<WorkerThread>, // before Start() is called. |thread_type_hint| is the preferred thread type; // the actual thread type depends on shutdown state and platform // capabilities. |task_tracker| is used to handle shutdown behavior of Tasks. + // |sequence_num| is an index that helps identifying this WorkerThread. // |predecessor_lock| is a lock that is allowed to be held when calling // methods on this WorkerThread. |backward_compatibility| indicates // whether backward compatibility is enabled. Either JoinForTesting() or @@ -108,6 +113,7 @@ class BASE_EXPORT WorkerThread : public RefCountedThreadSafe<WorkerThread>, WorkerThread(ThreadType thread_type_hint, std::unique_ptr<Delegate> delegate, TrackedRef<TaskTracker> task_tracker, + size_t sequence_num, const CheckedLock* predecessor_lock = nullptr); WorkerThread(const WorkerThread&) = delete; @@ -168,6 +174,8 @@ class BASE_EXPORT WorkerThread : public RefCountedThreadSafe<WorkerThread>, // this WorkerThread is currently in-use. Thread-safe. TimeTicks GetLastUsedTime() const; + size_t sequence_num() const { return sequence_num_; } + private: friend class RefCountedThreadSafe<WorkerThread>; class Thread; @@ -248,6 +256,8 @@ class BASE_EXPORT WorkerThread : public RefCountedThreadSafe<WorkerThread>, // Set once JoinForTesting() has been called. AtomicFlag join_called_for_testing_; + const size_t sequence_num_; + // Service thread task runner. scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner_; }; diff --git a/chromium/base/task/thread_pool/worker_thread_set.cc b/chromium/base/task/thread_pool/worker_thread_set.cc new file mode 100644 index 00000000000..3b43f409211 --- /dev/null +++ b/chromium/base/task/thread_pool/worker_thread_set.cc @@ -0,0 +1,62 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/task/thread_pool/worker_thread_set.h" + +#include "base/check_op.h" +#include "base/containers/contains.h" +#include "base/ranges/algorithm.h" +#include "base/task/thread_pool/worker_thread.h" + +namespace base::internal { + +bool WorkerThreadSet::Compare::operator()(const WorkerThread* a, + const WorkerThread* b) const { + return a->sequence_num() < b->sequence_num(); +} + +WorkerThreadSet::WorkerThreadSet() = default; + +WorkerThreadSet::~WorkerThreadSet() = default; + +void WorkerThreadSet::Insert(WorkerThread* worker) { + DCHECK(!Contains(worker)) << "WorkerThread already on stack"; + auto old_first = set_.begin(); + set_.insert(worker); + if (worker != *set_.begin()) + worker->BeginUnusedPeriod(); + else if (old_first != set_.end()) + (*old_first)->BeginUnusedPeriod(); +} + +WorkerThread* WorkerThreadSet::Take() { + if (IsEmpty()) + return nullptr; + WorkerThread* const worker = *set_.begin(); + set_.erase(set_.begin()); + if (!IsEmpty()) + (*set_.begin())->EndUnusedPeriod(); + return worker; +} + +WorkerThread* WorkerThreadSet::Peek() const { + if (IsEmpty()) + return nullptr; + return *set_.begin(); +} + +bool WorkerThreadSet::Contains(const WorkerThread* worker) const { + return set_.count(const_cast<WorkerThread*>(worker)) > 0; +} + +void WorkerThreadSet::Remove(const WorkerThread* worker) { + DCHECK(!IsEmpty()); + DCHECK_NE(worker, *set_.begin()); + auto it = set_.find(const_cast<WorkerThread*>(worker)); + DCHECK(it != set_.end()); + DCHECK_NE(TimeTicks(), (*it)->GetLastUsedTime()); + set_.erase(it); +} + +} // namespace base::internal diff --git a/chromium/base/task/thread_pool/worker_thread_set.h b/chromium/base/task/thread_pool/worker_thread_set.h new file mode 100644 index 00000000000..42773f90265 --- /dev/null +++ b/chromium/base/task/thread_pool/worker_thread_set.h @@ -0,0 +1,69 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TASK_THREAD_POOL_WORKER_THREAD_SET_H_ +#define BASE_TASK_THREAD_POOL_WORKER_THREAD_SET_H_ + +#include <stddef.h> + +#include <set> + +#include "base/base_export.h" + +namespace base { +namespace internal { + +class WorkerThread; + +// An ordered set of WorkerThreads which has custom logic to treat the worker at +// the front of the set as being "in-use" (so its time in that position doesn't +// count towards being inactive / reclaimable). Supports removal of arbitrary +// WorkerThreads. DCHECKs when a WorkerThread is inserted multiple times. +// WorkerThreads are not owned by the set. All operations are amortized +// O(log(n)). This class is NOT thread-safe. +class BASE_EXPORT WorkerThreadSet { + struct Compare { + bool operator()(const WorkerThread* a, const WorkerThread* b) const; + }; + + public: + WorkerThreadSet(); + WorkerThreadSet(const WorkerThreadSet&) = delete; + WorkerThreadSet& operator=(const WorkerThreadSet&) = delete; + ~WorkerThreadSet(); + + // Inserts |worker| in the set. |worker| must not already be on the set. Flags + // the WorkerThread previously at the front of the set, if it changed, or + // |worker| as unused. + void Insert(WorkerThread* worker); + + // Removes the front WorkerThread from the set and returns it. Returns nullptr + // if the set is empty. Flags the WorkerThread now at the front of the set, if + // any, as being in-use. + WorkerThread* Take(); + + // Returns the front WorkerThread from the set, nullptr if empty. + WorkerThread* Peek() const; + + // Returns true if |worker| is already in the set. + bool Contains(const WorkerThread* worker) const; + + // Removes |worker| from the set. Must not be invoked for the first worker + // on the set. + void Remove(const WorkerThread* worker); + + // Returns the number of WorkerThreads on the set. + size_t Size() const { return set_.size(); } + + // Returns true if the set is empty. + bool IsEmpty() const { return set_.empty(); } + + private: + std::set<WorkerThread*, Compare> set_; +}; + +} // namespace internal +} // namespace base + +#endif // BASE_TASK_THREAD_POOL_WORKER_THREAD_SET_H_ diff --git a/chromium/base/task/thread_pool/worker_thread_set_unittest.cc b/chromium/base/task/thread_pool/worker_thread_set_unittest.cc new file mode 100644 index 00000000000..b95af33b093 --- /dev/null +++ b/chromium/base/task/thread_pool/worker_thread_set_unittest.cc @@ -0,0 +1,242 @@ +// Copyright 2016 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/task/thread_pool/worker_thread_set.h" + +#include "base/check_op.h" +#include "base/memory/ref_counted.h" +#include "base/task/thread_pool/task_source.h" +#include "base/task/thread_pool/task_tracker.h" +#include "base/task/thread_pool/worker_thread.h" +#include "base/test/gtest_util.h" +#include "base/threading/platform_thread.h" +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base::internal { + +namespace { + +class MockWorkerThreadDelegate : public WorkerThread::Delegate { + public: + WorkerThread::ThreadLabel GetThreadLabel() const override { + return WorkerThread::ThreadLabel::DEDICATED; + } + void OnMainEntry(WorkerThread* worker) override {} + RegisteredTaskSource GetWork(WorkerThread* worker) override { + return nullptr; + } + void DidProcessTask(RegisteredTaskSource task_source) override { + ADD_FAILURE() << "Unexpected call to DidRunTask()"; + } + TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); } +}; + +class ThreadPoolWorkerSetTest : public testing::Test { + protected: + void SetUp() override { + worker_a_ = MakeRefCounted<WorkerThread>( + ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(), + task_tracker_.GetTrackedRef(), 0); + ASSERT_TRUE(worker_a_); + worker_b_ = MakeRefCounted<WorkerThread>( + ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(), + task_tracker_.GetTrackedRef(), 1); + ASSERT_TRUE(worker_b_); + worker_c_ = MakeRefCounted<WorkerThread>( + ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(), + task_tracker_.GetTrackedRef(), 2); + ASSERT_TRUE(worker_c_); + } + + private: + TaskTracker task_tracker_; + + protected: + scoped_refptr<WorkerThread> worker_a_; + scoped_refptr<WorkerThread> worker_b_; + scoped_refptr<WorkerThread> worker_c_; +}; + +} // namespace + +// Verify that Insert() and Take() add/remove values in FIFO order. +TEST_F(ThreadPoolWorkerSetTest, InsertTake) { + WorkerThreadSet set; + EXPECT_EQ(nullptr, set.Take()); + + EXPECT_TRUE(set.IsEmpty()); + EXPECT_EQ(0U, set.Size()); + + set.Insert(worker_a_.get()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(1U, set.Size()); + + set.Insert(worker_b_.get()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(2U, set.Size()); + + set.Insert(worker_c_.get()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(3U, set.Size()); + + WorkerThread* idle_worker = set.Take(); + EXPECT_EQ(idle_worker, worker_a_.get()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(2U, set.Size()); + + set.Insert(idle_worker); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(3U, set.Size()); + + EXPECT_EQ(idle_worker, set.Take()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(2U, set.Size()); + + EXPECT_TRUE(set.Take()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(1U, set.Size()); + + EXPECT_TRUE(set.Take()); + EXPECT_TRUE(set.IsEmpty()); + EXPECT_EQ(0U, set.Size()); + + EXPECT_EQ(nullptr, set.Take()); +} + +// Verify that Peek() returns the correct values in FIFO order. +TEST_F(ThreadPoolWorkerSetTest, PeekPop) { + WorkerThreadSet set; + EXPECT_EQ(nullptr, set.Peek()); + + EXPECT_TRUE(set.IsEmpty()); + EXPECT_EQ(0U, set.Size()); + + set.Insert(worker_a_.get()); + EXPECT_EQ(worker_a_.get(), set.Peek()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(1U, set.Size()); + + set.Insert(worker_b_.get()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(2U, set.Size()); + + set.Insert(worker_c_.get()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(3U, set.Size()); + + WorkerThread* idle_worker = set.Take(); + EXPECT_EQ(worker_a_.get(), idle_worker); + EXPECT_EQ(worker_b_.get(), set.Peek()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(2U, set.Size()); + + EXPECT_EQ(worker_b_.get(), set.Take()); + EXPECT_EQ(worker_c_.get(), set.Peek()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(1U, set.Size()); + + EXPECT_EQ(worker_c_.get(), set.Take()); + EXPECT_TRUE(set.IsEmpty()); + EXPECT_EQ(0U, set.Size()); + + EXPECT_EQ(nullptr, set.Peek()); +} + +// Verify that Contains() returns true for workers on the set. +TEST_F(ThreadPoolWorkerSetTest, Contains) { + WorkerThreadSet set; + EXPECT_FALSE(set.Contains(worker_a_.get())); + EXPECT_FALSE(set.Contains(worker_b_.get())); + EXPECT_FALSE(set.Contains(worker_c_.get())); + + set.Insert(worker_a_.get()); + EXPECT_TRUE(set.Contains(worker_a_.get())); + EXPECT_FALSE(set.Contains(worker_b_.get())); + EXPECT_FALSE(set.Contains(worker_c_.get())); + + set.Insert(worker_b_.get()); + EXPECT_TRUE(set.Contains(worker_a_.get())); + EXPECT_TRUE(set.Contains(worker_b_.get())); + EXPECT_FALSE(set.Contains(worker_c_.get())); + + set.Insert(worker_c_.get()); + EXPECT_TRUE(set.Contains(worker_a_.get())); + EXPECT_TRUE(set.Contains(worker_b_.get())); + EXPECT_TRUE(set.Contains(worker_c_.get())); + + WorkerThread* idle_worker = set.Take(); + EXPECT_EQ(idle_worker, worker_a_.get()); + EXPECT_FALSE(set.Contains(worker_a_.get())); + EXPECT_TRUE(set.Contains(worker_b_.get())); + EXPECT_TRUE(set.Contains(worker_c_.get())); + + set.Take(); + + set.Take(); + EXPECT_FALSE(set.Contains(worker_a_.get())); + EXPECT_FALSE(set.Contains(worker_b_.get())); + EXPECT_FALSE(set.Contains(worker_c_.get())); +} + +// Verify that a value can be removed by Remove(). +TEST_F(ThreadPoolWorkerSetTest, Remove) { + WorkerThreadSet set; + EXPECT_TRUE(set.IsEmpty()); + EXPECT_EQ(0U, set.Size()); + + set.Insert(worker_a_.get()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(1U, set.Size()); + + set.Insert(worker_b_.get()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(2U, set.Size()); + + set.Insert(worker_c_.get()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(3U, set.Size()); + + set.Remove(worker_b_.get()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(2U, set.Size()); + + EXPECT_EQ(worker_a_.get(), set.Take()); + EXPECT_FALSE(set.IsEmpty()); + EXPECT_EQ(1U, set.Size()); + + EXPECT_EQ(worker_c_.get(), set.Take()); + EXPECT_TRUE(set.IsEmpty()); + EXPECT_EQ(0U, set.Size()); +} + +// Verify that a value can be pushed again after it has been removed. +TEST_F(ThreadPoolWorkerSetTest, PushAfterRemove) { + WorkerThreadSet set; + EXPECT_EQ(0U, set.Size()); + + set.Insert(worker_a_.get()); + EXPECT_EQ(1U, set.Size()); + + // Need to also push worker B for this test as it's illegal to Remove() the + // front of the set. + set.Insert(worker_b_.get()); + EXPECT_EQ(2U, set.Size()); + + set.Remove(worker_b_.get()); + worker_b_->EndUnusedPeriod(); + EXPECT_EQ(1U, set.Size()); + + set.Insert(worker_b_.get()); + EXPECT_EQ(2U, set.Size()); +} + +// Verify that Insert() DCHECKs when a value is inserted twice. +TEST_F(ThreadPoolWorkerSetTest, PushTwice) { + WorkerThreadSet set; + set.Insert(worker_a_.get()); + EXPECT_DCHECK_DEATH({ set.Insert(worker_a_.get()); }); +} + +} // namespace base::internal diff --git a/chromium/base/task/thread_pool/worker_thread_stack.cc b/chromium/base/task/thread_pool/worker_thread_stack.cc deleted file mode 100644 index 63bc475ca40..00000000000 --- a/chromium/base/task/thread_pool/worker_thread_stack.cc +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/task/thread_pool/worker_thread_stack.h" - -#include "base/check_op.h" -#include "base/containers/contains.h" -#include "base/ranges/algorithm.h" -#include "base/task/thread_pool/worker_thread.h" - -namespace base { -namespace internal { - -WorkerThreadStack::WorkerThreadStack() = default; - -WorkerThreadStack::~WorkerThreadStack() = default; - -void WorkerThreadStack::Push(WorkerThread* worker) { - DCHECK(!Contains(worker)) << "WorkerThread already on stack"; - if (!IsEmpty()) - stack_.back()->BeginUnusedPeriod(); - stack_.push_back(worker); -} - -WorkerThread* WorkerThreadStack::Pop() { - if (IsEmpty()) - return nullptr; - WorkerThread* const worker = stack_.back(); - stack_.pop_back(); - if (!IsEmpty()) - stack_.back()->EndUnusedPeriod(); - return worker; -} - -WorkerThread* WorkerThreadStack::Peek() const { - if (IsEmpty()) - return nullptr; - return stack_.back(); -} - -bool WorkerThreadStack::Contains(const WorkerThread* worker) const { - return base::Contains(stack_, worker); -} - -void WorkerThreadStack::Remove(const WorkerThread* worker) { - DCHECK(!IsEmpty()); - DCHECK_NE(worker, stack_.back()); - auto it = ranges::find(stack_, worker); - DCHECK(it != stack_.end()); - DCHECK_NE(TimeTicks(), (*it)->GetLastUsedTime()); - stack_.erase(it); -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/task/thread_pool/worker_thread_stack.h b/chromium/base/task/thread_pool/worker_thread_stack.h deleted file mode 100644 index 024be8971e6..00000000000 --- a/chromium/base/task/thread_pool/worker_thread_stack.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_TASK_THREAD_POOL_WORKER_THREAD_STACK_H_ -#define BASE_TASK_THREAD_POOL_WORKER_THREAD_STACK_H_ - -#include <stddef.h> - -#include <vector> - -#include "base/base_export.h" - -namespace base { -namespace internal { - -class WorkerThread; - -// A stack of WorkerThreads which has custom logic to treat the worker on top -// of the stack as being "in-use" (so its time in that position doesn't count -// towards being inactive / reclaimable). Supports removal of arbitrary -// WorkerThreads. DCHECKs when a WorkerThread is inserted multiple times. -// WorkerThreads are not owned by the stack. Push() is amortized O(1). Pop(), -// Peek(), Size() and Empty() are O(1). Contains() and Remove() are O(n). This -// class is NOT thread-safe. -class BASE_EXPORT WorkerThreadStack { - public: - WorkerThreadStack(); - WorkerThreadStack(const WorkerThreadStack&) = delete; - WorkerThreadStack& operator=(const WorkerThreadStack&) = delete; - ~WorkerThreadStack(); - - // Inserts |worker| at the top of the stack. |worker| must not already be on - // the stack. Flags the WorkerThread previously on top of the stack, if - // any, as unused. - void Push(WorkerThread* worker); - - // Removes the top WorkerThread from the stack and returns it. Returns - // nullptr if the stack is empty. Flags the WorkerThread now on top of the - // stack, if any, as being in-use. - WorkerThread* Pop(); - - // Returns the top WorkerThread from the stack, nullptr if empty. - WorkerThread* Peek() const; - - // Returns true if |worker| is already on the stack. - bool Contains(const WorkerThread* worker) const; - - // Removes |worker| from the stack. Must not be invoked for the first worker - // on the stack. - void Remove(const WorkerThread* worker); - - // Returns the number of WorkerThreads on the stack. - size_t Size() const { return stack_.size(); } - - // Returns true if the stack is empty. - bool IsEmpty() const { return stack_.empty(); } - - private: - std::vector<WorkerThread*> stack_; -}; - -} // namespace internal -} // namespace base - -#endif // BASE_TASK_THREAD_POOL_WORKER_THREAD_STACK_H_ diff --git a/chromium/base/task/thread_pool/worker_thread_stack_unittest.cc b/chromium/base/task/thread_pool/worker_thread_stack_unittest.cc deleted file mode 100644 index 1047b2f3369..00000000000 --- a/chromium/base/task/thread_pool/worker_thread_stack_unittest.cc +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2016 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/task/thread_pool/worker_thread_stack.h" - -#include "base/check_op.h" -#include "base/memory/ref_counted.h" -#include "base/task/thread_pool/task_source.h" -#include "base/task/thread_pool/task_tracker.h" -#include "base/task/thread_pool/worker_thread.h" -#include "base/test/gtest_util.h" -#include "base/threading/platform_thread.h" -#include "base/time/time.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { -namespace internal { - -namespace { - -class MockWorkerThreadDelegate : public WorkerThread::Delegate { - public: - WorkerThread::ThreadLabel GetThreadLabel() const override { - return WorkerThread::ThreadLabel::DEDICATED; - } - void OnMainEntry(WorkerThread* worker) override {} - RegisteredTaskSource GetWork(WorkerThread* worker) override { - return nullptr; - } - void DidProcessTask(RegisteredTaskSource task_source) override { - ADD_FAILURE() << "Unexpected call to DidRunTask()"; - } - TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); } -}; - -class ThreadPoolWorkerStackTest : public testing::Test { - protected: - void SetUp() override { - worker_a_ = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(), - task_tracker_.GetTrackedRef()); - ASSERT_TRUE(worker_a_); - worker_b_ = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(), - task_tracker_.GetTrackedRef()); - ASSERT_TRUE(worker_b_); - worker_c_ = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(), - task_tracker_.GetTrackedRef()); - ASSERT_TRUE(worker_c_); - } - - private: - TaskTracker task_tracker_; - - protected: - scoped_refptr<WorkerThread> worker_a_; - scoped_refptr<WorkerThread> worker_b_; - scoped_refptr<WorkerThread> worker_c_; -}; - -} // namespace - -// Verify that Push() and Pop() add/remove values in FIFO order. -TEST_F(ThreadPoolWorkerStackTest, PushPop) { - WorkerThreadStack stack; - EXPECT_EQ(nullptr, stack.Pop()); - - EXPECT_TRUE(stack.IsEmpty()); - EXPECT_EQ(0U, stack.Size()); - - stack.Push(worker_a_.get()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(1U, stack.Size()); - - stack.Push(worker_b_.get()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(2U, stack.Size()); - - stack.Push(worker_c_.get()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(3U, stack.Size()); - - EXPECT_EQ(worker_c_.get(), stack.Pop()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(2U, stack.Size()); - - stack.Push(worker_c_.get()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(3U, stack.Size()); - - EXPECT_EQ(worker_c_.get(), stack.Pop()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(2U, stack.Size()); - - EXPECT_EQ(worker_b_.get(), stack.Pop()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(1U, stack.Size()); - - EXPECT_EQ(worker_a_.get(), stack.Pop()); - EXPECT_TRUE(stack.IsEmpty()); - EXPECT_EQ(0U, stack.Size()); - - EXPECT_EQ(nullptr, stack.Pop()); -} - -// Verify that Peek() returns the correct values in FIFO order. -TEST_F(ThreadPoolWorkerStackTest, PeekPop) { - WorkerThreadStack stack; - EXPECT_EQ(nullptr, stack.Peek()); - - EXPECT_TRUE(stack.IsEmpty()); - EXPECT_EQ(0U, stack.Size()); - - stack.Push(worker_a_.get()); - EXPECT_EQ(worker_a_.get(), stack.Peek()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(1U, stack.Size()); - - stack.Push(worker_b_.get()); - EXPECT_EQ(worker_b_.get(), stack.Peek()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(2U, stack.Size()); - - stack.Push(worker_c_.get()); - EXPECT_EQ(worker_c_.get(), stack.Peek()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(3U, stack.Size()); - - EXPECT_EQ(worker_c_.get(), stack.Pop()); - EXPECT_EQ(worker_b_.get(), stack.Peek()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(2U, stack.Size()); - - EXPECT_EQ(worker_b_.get(), stack.Pop()); - EXPECT_EQ(worker_a_.get(), stack.Peek()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(1U, stack.Size()); - - EXPECT_EQ(worker_a_.get(), stack.Pop()); - EXPECT_TRUE(stack.IsEmpty()); - EXPECT_EQ(0U, stack.Size()); - - EXPECT_EQ(nullptr, stack.Peek()); -} - -// Verify that Contains() returns true for workers on the stack. -TEST_F(ThreadPoolWorkerStackTest, Contains) { - WorkerThreadStack stack; - EXPECT_FALSE(stack.Contains(worker_a_.get())); - EXPECT_FALSE(stack.Contains(worker_b_.get())); - EXPECT_FALSE(stack.Contains(worker_c_.get())); - - stack.Push(worker_a_.get()); - EXPECT_TRUE(stack.Contains(worker_a_.get())); - EXPECT_FALSE(stack.Contains(worker_b_.get())); - EXPECT_FALSE(stack.Contains(worker_c_.get())); - - stack.Push(worker_b_.get()); - EXPECT_TRUE(stack.Contains(worker_a_.get())); - EXPECT_TRUE(stack.Contains(worker_b_.get())); - EXPECT_FALSE(stack.Contains(worker_c_.get())); - - stack.Push(worker_c_.get()); - EXPECT_TRUE(stack.Contains(worker_a_.get())); - EXPECT_TRUE(stack.Contains(worker_b_.get())); - EXPECT_TRUE(stack.Contains(worker_c_.get())); - - stack.Pop(); - EXPECT_TRUE(stack.Contains(worker_a_.get())); - EXPECT_TRUE(stack.Contains(worker_b_.get())); - EXPECT_FALSE(stack.Contains(worker_c_.get())); - - stack.Pop(); - EXPECT_TRUE(stack.Contains(worker_a_.get())); - EXPECT_FALSE(stack.Contains(worker_b_.get())); - EXPECT_FALSE(stack.Contains(worker_c_.get())); - - stack.Pop(); - EXPECT_FALSE(stack.Contains(worker_a_.get())); - EXPECT_FALSE(stack.Contains(worker_b_.get())); - EXPECT_FALSE(stack.Contains(worker_c_.get())); -} - -// Verify that a value can be removed by Remove(). -TEST_F(ThreadPoolWorkerStackTest, Remove) { - WorkerThreadStack stack; - EXPECT_TRUE(stack.IsEmpty()); - EXPECT_EQ(0U, stack.Size()); - - stack.Push(worker_a_.get()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(1U, stack.Size()); - - stack.Push(worker_b_.get()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(2U, stack.Size()); - - stack.Push(worker_c_.get()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(3U, stack.Size()); - - stack.Remove(worker_b_.get()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(2U, stack.Size()); - - EXPECT_EQ(worker_c_.get(), stack.Pop()); - EXPECT_FALSE(stack.IsEmpty()); - EXPECT_EQ(1U, stack.Size()); - - EXPECT_EQ(worker_a_.get(), stack.Pop()); - EXPECT_TRUE(stack.IsEmpty()); - EXPECT_EQ(0U, stack.Size()); -} - -// Verify that a value can be pushed again after it has been removed. -TEST_F(ThreadPoolWorkerStackTest, PushAfterRemove) { - WorkerThreadStack stack; - EXPECT_EQ(0U, stack.Size()); - - stack.Push(worker_a_.get()); - EXPECT_EQ(1U, stack.Size()); - - // Need to also push worker B for this test as it's illegal to Remove() the - // top of the stack. - stack.Push(worker_b_.get()); - EXPECT_EQ(2U, stack.Size()); - - stack.Remove(worker_a_.get()); - EXPECT_EQ(1U, stack.Size()); - - stack.Push(worker_a_.get()); - EXPECT_EQ(2U, stack.Size()); -} - -// Verify that Push() DCHECKs when a value is inserted twice. -TEST_F(ThreadPoolWorkerStackTest, PushTwice) { - WorkerThreadStack stack; - stack.Push(worker_a_.get()); - EXPECT_DCHECK_DEATH({ stack.Push(worker_a_.get()); }); -} - -} // namespace internal -} // namespace base diff --git a/chromium/base/task/thread_pool/worker_thread_unittest.cc b/chromium/base/task/thread_pool/worker_thread_unittest.cc index 7d452df81d1..54c40918b01 100644 --- a/chromium/base/task/thread_pool/worker_thread_unittest.cc +++ b/chromium/base/task/thread_pool/worker_thread_unittest.cc @@ -94,7 +94,7 @@ class ThreadPoolWorkerTest : public testing::TestWithParam<int> { void SetUp() override { worker_ = MakeRefCounted<WorkerThread>( ThreadType::kDefault, std::make_unique<TestWorkerThreadDelegate>(this), - task_tracker_.GetTrackedRef()); + task_tracker_.GetTrackedRef(), 0); ASSERT_TRUE(worker_); worker_->Start(service_thread_.task_runner()); worker_set_.Signal(); @@ -201,6 +201,7 @@ class ThreadPoolWorkerTest : public testing::TestWithParam<int> { BindOnce(&ThreadPoolWorkerTest::RunTaskCallback, Unretained(outer_)), TimeTicks::Now(), TimeDelta()); + sequence_transaction.WillPushImmediateTask(); EXPECT_TRUE(outer_->task_tracker_.WillPostTask( &task, sequence->shutdown_behavior())); sequence_transaction.PushImmediateTask(std::move(task)); @@ -487,9 +488,11 @@ class ControllableCleanupDelegate : public WorkerThreadDefaultDelegate { Unretained(&controls_->work_processed_), Unretained(&controls_->work_running_)), TimeTicks::Now(), TimeDelta()); + auto transaction = sequence->BeginTransaction(); + transaction.WillPushImmediateTask(); EXPECT_TRUE( task_tracker_->WillPostTask(&task, sequence->shutdown_behavior())); - sequence->BeginTransaction().PushImmediateTask(std::move(task)); + transaction.PushImmediateTask(std::move(task)); auto registered_task_source = task_tracker_->RegisterTaskSource(std::move(sequence)); EXPECT_TRUE(registered_task_source); @@ -556,8 +559,9 @@ TEST(ThreadPoolWorkerTest, WorkerCleanupFromGetWork) { delegate->controls(); controls->set_can_cleanup(true); EXPECT_CALL(*delegate, OnMainEntry(_)); - auto worker = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, WrapUnique(delegate), task_tracker.GetTrackedRef()); + auto worker = + MakeRefCounted<WorkerThread>(ThreadType::kDefault, WrapUnique(delegate), + task_tracker.GetTrackedRef(), 0); worker->Start(service_thread.task_runner()); worker->WakeUp(); controls->WaitForWorkToRun(); @@ -583,8 +587,9 @@ TEST(ThreadPoolWorkerTest, WorkerCleanupDuringWork) { controls->HaveWorkBlock(); - auto worker = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, std::move(delegate), task_tracker.GetTrackedRef()); + auto worker = + MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate), + task_tracker.GetTrackedRef(), 0); worker->Start(service_thread.task_runner()); worker->WakeUp(); @@ -609,8 +614,9 @@ TEST(ThreadPoolWorkerTest, WorkerCleanupDuringWait) { scoped_refptr<ControllableCleanupDelegate::Controls> controls = delegate->controls(); - auto worker = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, std::move(delegate), task_tracker.GetTrackedRef()); + auto worker = + MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate), + task_tracker.GetTrackedRef(), 0); worker->Start(service_thread.task_runner()); worker->WakeUp(); @@ -636,8 +642,9 @@ TEST(ThreadPoolWorkerTest, WorkerCleanupDuringShutdown) { controls->HaveWorkBlock(); - auto worker = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, std::move(delegate), task_tracker.GetTrackedRef()); + auto worker = + MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate), + task_tracker.GetTrackedRef(), 0); worker->Start(service_thread.task_runner()); worker->WakeUp(); @@ -665,8 +672,9 @@ TEST(ThreadPoolWorkerTest, CleanupBeforeStart) { delegate->controls(); controls->set_expect_get_work(false); - auto worker = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, std::move(delegate), task_tracker.GetTrackedRef()); + auto worker = + MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate), + task_tracker.GetTrackedRef(), 0); worker->Cleanup(); worker->Start(service_thread.task_runner()); @@ -718,8 +726,9 @@ TEST(ThreadPoolWorkerTest, WorkerCleanupDuringJoin) { controls->HaveWorkBlock(); - auto worker = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, std::move(delegate), task_tracker.GetTrackedRef()); + auto worker = + MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate), + task_tracker.GetTrackedRef(), 0); worker->Start(service_thread.task_runner()); worker->WakeUp(); @@ -807,7 +816,7 @@ TEST(ThreadPoolWorkerTest, BumpThreadTypeOfAliveThreadDuringShutdown) { delegate_raw->SetExpectedThreadType(ThreadType::kBackground); auto worker = MakeRefCounted<WorkerThread>(ThreadType::kBackground, std::move(delegate), - task_tracker.GetTrackedRef()); + task_tracker.GetTrackedRef(), 0); worker->Start(service_thread.task_runner()); // Verify that the initial thread type is kBackground (or kNormal if thread @@ -860,8 +869,9 @@ TEST(ThreadPoolWorkerTest, WorkerThreadObserver) { service_thread_options.message_pump_type = MessagePumpType::IO; service_thread.StartWithOptions(std::move(service_thread_options)); auto delegate = std::make_unique<VerifyCallsToObserverDelegate>(&observer); - auto worker = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, std::move(delegate), task_tracker.GetTrackedRef()); + auto worker = + MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate), + task_tracker.GetTrackedRef(), 0); EXPECT_CALL(observer, OnWorkerThreadMainEntry()); worker->Start(service_thread.task_runner(), &observer); worker->Cleanup(); @@ -916,8 +926,11 @@ class WorkerThreadThreadCacheDelegate : public WorkerThreadDefaultDelegate { TEST(ThreadPoolWorkerThreadCachePurgeTest, Purge) { // Make sure the thread cache is enabled in the main partition. - allocator_shim::internal::PartitionAllocMalloc::Allocator() - ->EnableThreadCacheIfSupported(); + if (!allocator_shim::internal::PartitionAllocMalloc::Allocator() + ->thread_cache_for_testing()) { + allocator_shim::internal::PartitionAllocMalloc::Allocator() + ->EnableThreadCacheIfSupported(); + } Thread service_thread = Thread("ServiceThread"); Thread::Options service_thread_options; @@ -926,8 +939,9 @@ TEST(ThreadPoolWorkerThreadCachePurgeTest, Purge) { TaskTracker task_tracker; auto delegate = std::make_unique<WorkerThreadThreadCacheDelegate>(); auto* delegate_raw = delegate.get(); - auto worker = MakeRefCounted<WorkerThread>( - ThreadType::kDefault, std::move(delegate), task_tracker.GetTrackedRef()); + auto worker = + MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate), + task_tracker.GetTrackedRef(), 0); // Wake up before the thread is started to make sure the first sleep is short. worker->WakeUp(); worker->Start(service_thread.task_runner(), nullptr); |