summaryrefslogtreecommitdiffstats
path: root/chromium/base/task
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2023-02-13 16:03:23 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2023-05-26 11:26:35 +0000
commit813d9ae984a99e739b99cf694a9d5b24d0a6b7a7 (patch)
tree60c14d40d77a3c702c8a72887662d97c0b8f3e99 /chromium/base/task
parenteb596ba9fe579987eb93f6b4021ca156885b48c2 (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')
-rw-r--r--chromium/base/task/bind_post_task_unittest.cc8
-rw-r--r--chromium/base/task/bind_post_task_unittest.nc6
-rw-r--r--chromium/base/task/cancelable_task_tracker.cc29
-rw-r--r--chromium/base/task/common/checked_lock.h7
-rw-r--r--chromium/base/task/common/checked_lock_impl.cc1
-rw-r--r--chromium/base/task/common/operations_controller_unittest.cc17
-rw-r--r--chromium/base/task/common/task_annotator.cc108
-rw-r--r--chromium/base/task/common/task_annotator.h55
-rw-r--r--chromium/base/task/common/task_annotator_unittest.cc56
-rw-r--r--chromium/base/task/current_thread.cc4
-rw-r--r--chromium/base/task/current_thread.h14
-rw-r--r--chromium/base/task/sequence_manager/associated_thread_id.cc16
-rw-r--r--chromium/base/task/sequence_manager/associated_thread_id.h32
-rw-r--r--chromium/base/task/sequence_manager/delayed_task_handle_delegate.h3
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager.cc2
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager.h26
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl.cc100
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl.h68
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc87
-rw-r--r--chromium/base/task/sequence_manager/sequenced_task_source.h3
-rw-r--r--chromium/base/task/sequence_manager/task_queue_impl.cc11
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector.h1
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector_logic.h36
-rw-r--r--chromium/base/task/sequence_manager/thread_controller.cc30
-rw-r--r--chromium/base/task/sequence_manager/thread_controller.h19
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_impl.cc8
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc78
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.h3
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc38
-rw-r--r--chromium/base/task/sequence_manager/wake_up_queue.cc2
-rw-r--r--chromium/base/task/sequence_manager/wake_up_queue.h2
-rw-r--r--chromium/base/task/sequence_manager/wake_up_queue_unittest.cc2
-rw-r--r--chromium/base/task/sequence_manager/work_deduplicator.cc1
-rw-r--r--chromium/base/task/sequence_manager/work_queue_sets_unittest.cc2
-rw-r--r--chromium/base/task/sequence_manager/work_queue_unittest.cc2
-rw-r--r--chromium/base/task/sequenced_task_runner.h4
-rw-r--r--chromium/base/task/sequenced_task_runner_unittest.cc4
-rw-r--r--chromium/base/task/single_thread_task_executor.cc2
-rw-r--r--chromium/base/task/single_thread_task_executor_unittest.cc302
-rw-r--r--chromium/base/task/task_features.cc41
-rw-r--r--chromium/base/task/task_features.h26
-rw-r--r--chromium/base/task/task_runner.cc2
-rw-r--r--chromium/base/task/task_runner_unittest.cc16
-rw-r--r--chromium/base/task/task_runner_util.h56
-rw-r--r--chromium/base/task/task_runner_util_unittest.cc158
-rw-r--r--chromium/base/task/thread_pool/delayed_task_manager.cc60
-rw-r--r--chromium/base/task/thread_pool/delayed_task_manager.h13
-rw-r--r--chromium/base/task/thread_pool/delayed_task_manager_unittest.cc17
-rw-r--r--chromium/base/task/thread_pool/environment_config.cc21
-rw-r--r--chromium/base/task/thread_pool/environment_config.h10
-rw-r--r--chromium/base/task/thread_pool/environment_config_unittest.cc9
-rw-r--r--chromium/base/task/thread_pool/job_task_source.cc12
-rw-r--r--chromium/base/task/thread_pool/job_task_source.h3
-rw-r--r--chromium/base/task/thread_pool/pooled_sequenced_task_runner.cc4
-rw-r--r--chromium/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc37
-rw-r--r--chromium/base/task/thread_pool/pooled_single_thread_task_runner_manager_unittest.cc69
-rw-r--r--chromium/base/task/thread_pool/priority_queue.cc2
-rw-r--r--chromium/base/task/thread_pool/priority_queue.h2
-rw-r--r--chromium/base/task/thread_pool/priority_queue_unittest.cc4
-rw-r--r--chromium/base/task/thread_pool/sequence.cc233
-rw-r--r--chromium/base/task/thread_pool/sequence.h107
-rw-r--r--chromium/base/task/thread_pool/sequence_unittest.cc352
-rw-r--r--chromium/base/task/thread_pool/task_source.cc7
-rw-r--r--chromium/base/task/thread_pool/task_source.h27
-rw-r--r--chromium/base/task/thread_pool/task_tracker.cc23
-rw-r--r--chromium/base/task/thread_pool/task_tracker.h6
-rw-r--r--chromium/base/task/thread_pool/task_tracker_unittest.cc39
-rw-r--r--chromium/base/task/thread_pool/test_task_factory.cc22
-rw-r--r--chromium/base/task/thread_pool/test_utils.cc6
-rw-r--r--chromium/base/task/thread_pool/test_utils.h9
-rw-r--r--chromium/base/task/thread_pool/thread_group.cc26
-rw-r--r--chromium/base/task/thread_pool/thread_group.h9
-rw-r--r--chromium/base/task/thread_pool/thread_group_impl.cc170
-rw-r--r--chromium/base/task/thread_pool/thread_group_impl.h28
-rw-r--r--chromium/base/task/thread_pool/thread_group_impl_unittest.cc3
-rw-r--r--chromium/base/task/thread_pool/thread_group_native.cc187
-rw-r--r--chromium/base/task/thread_pool/thread_group_native.h86
-rw-r--r--chromium/base/task/thread_pool/thread_group_native_mac.h66
-rw-r--r--chromium/base/task/thread_pool/thread_group_native_mac.mm56
-rw-r--r--chromium/base/task/thread_pool/thread_group_native_win.cc105
-rw-r--r--chromium/base/task/thread_pool/thread_group_native_win.h70
-rw-r--r--chromium/base/task/thread_pool/thread_group_unittest.cc150
-rw-r--r--chromium/base/task/thread_pool/thread_pool_impl.cc121
-rw-r--r--chromium/base/task/thread_pool/thread_pool_impl.h2
-rw-r--r--chromium/base/task/thread_pool/thread_pool_impl_unittest.cc324
-rw-r--r--chromium/base/task/thread_pool/thread_pool_instance.cc18
-rw-r--r--chromium/base/task/thread_pool/thread_pool_instance.h6
-rw-r--r--chromium/base/task/thread_pool/worker_thread.cc78
-rw-r--r--chromium/base/task/thread_pool/worker_thread.h10
-rw-r--r--chromium/base/task/thread_pool/worker_thread_set.cc62
-rw-r--r--chromium/base/task/thread_pool/worker_thread_set.h69
-rw-r--r--chromium/base/task/thread_pool/worker_thread_set_unittest.cc242
-rw-r--r--chromium/base/task/thread_pool/worker_thread_stack.cc56
-rw-r--r--chromium/base/task/thread_pool/worker_thread_stack.h66
-rw-r--r--chromium/base/task/thread_pool/worker_thread_stack_unittest.cc245
-rw-r--r--chromium/base/task/thread_pool/worker_thread_unittest.cc56
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);