#include "base/task/thread_pool/thread_group.h"
#include <utility>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/task/task_features.h"
#include "base/task/thread_pool/task_tracker.h"
#include "build/build_config.h"
#include "third_party/abseil-cpp/absl/base/attributes.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/com_init_check_hook.h"
#include "base/win/scoped_winrt_initializer.h"
#endif
namespace base {
namespace internal {
namespace {
ABSL_CONST_INIT thread_local const ThreadGroup* current_thread_group = nullptr;
}
constexpr ThreadGroup::YieldSortKey ThreadGroup::kMaxYieldSortKey;
void ThreadGroup::BaseScopedCommandsExecutor::ScheduleReleaseTaskSource(
RegisteredTaskSource task_source) {
task_sources_to_release_.push_back(std::move(task_source));
}
ThreadGroup::BaseScopedCommandsExecutor::BaseScopedCommandsExecutor() = default;
ThreadGroup::BaseScopedCommandsExecutor::~BaseScopedCommandsExecutor() {
CheckedLock::AssertNoLockHeldOnCurrentThread();
}
ThreadGroup::ScopedReenqueueExecutor::ScopedReenqueueExecutor() = default;
ThreadGroup::ScopedReenqueueExecutor::~ScopedReenqueueExecutor() {
if (destination_thread_group_) {
destination_thread_group_->PushTaskSourceAndWakeUpWorkers(
std::move(transaction_with_task_source_.value()));
}
}
void ThreadGroup::ScopedReenqueueExecutor::
SchedulePushTaskSourceAndWakeUpWorkers(
TransactionWithRegisteredTaskSource transaction_with_task_source,
ThreadGroup* destination_thread_group) {
DCHECK(destination_thread_group);
DCHECK(!destination_thread_group_);
DCHECK(!transaction_with_task_source_);
transaction_with_task_source_.emplace(
std::move(transaction_with_task_source));
destination_thread_group_ = destination_thread_group;
}
ThreadGroup::ThreadGroup(TrackedRef<TaskTracker> task_tracker,
TrackedRef<Delegate> delegate,
ThreadGroup* predecessor_thread_group)
: task_tracker_(std::move(task_tracker)),
delegate_(std::move(delegate)),
lock_(predecessor_thread_group ? &predecessor_thread_group->lock_
: nullptr) {
DCHECK(task_tracker_);
}
ThreadGroup::~ThreadGroup() = default;
void ThreadGroup::BindToCurrentThread() {
DCHECK(!CurrentThreadHasGroup());
current_thread_group = this;
}
void ThreadGroup::UnbindFromCurrentThread() {
DCHECK(IsBoundToCurrentThread());
current_thread_group = nullptr;
}
bool ThreadGroup::IsBoundToCurrentThread() const {
return current_thread_group == this;
}
void ThreadGroup::Start() {
CheckedAutoLock auto_lock(lock_);
}
size_t
ThreadGroup::GetNumAdditionalWorkersForBestEffortTaskSourcesLockRequired()
const {
const size_t num_queued =
priority_queue_.GetNumTaskSourcesWithPriority(TaskPriority::BEST_EFFORT);
if (num_queued == 0 ||
!task_tracker_->CanRunPriority(TaskPriority::BEST_EFFORT)) {
return 0U;
}
if (priority_queue_.PeekSortKey().priority() == TaskPriority::BEST_EFFORT) {
return std::max<size_t>(
1, num_queued +
priority_queue_.PeekTaskSource()->GetRemainingConcurrency() - 1);
}
return num_queued;
}
size_t
ThreadGroup::GetNumAdditionalWorkersForForegroundTaskSourcesLockRequired()
const {
const size_t num_queued = priority_queue_.GetNumTaskSourcesWithPriority(
TaskPriority::USER_VISIBLE) +
priority_queue_.GetNumTaskSourcesWithPriority(
TaskPriority::USER_BLOCKING);
if (num_queued == 0 ||
!task_tracker_->CanRunPriority(TaskPriority::HIGHEST)) {
return 0U;
}
auto priority = priority_queue_.PeekSortKey().priority();
if (priority == TaskPriority::USER_VISIBLE ||
priority == TaskPriority::USER_BLOCKING) {
return std::max<size_t>(
1, num_queued +
priority_queue_.PeekTaskSource()->GetRemainingConcurrency() - 1);
}
return num_queued;
}
RegisteredTaskSource ThreadGroup::RemoveTaskSource(
const TaskSource& task_source) {
CheckedAutoLock auto_lock(lock_);
return priority_queue_.RemoveTaskSource(task_source);
}
void ThreadGroup::ReEnqueueTaskSourceLockRequired(
BaseScopedCommandsExecutor* workers_executor,
ScopedReenqueueExecutor* reenqueue_executor,
TransactionWithRegisteredTaskSource transaction_with_task_source) {
ThreadGroup* destination_thread_group = delegate_->GetThreadGroupForTraits(
transaction_with_task_source.transaction.traits());
bool push_to_immediate_queue =
transaction_with_task_source.task_source.WillReEnqueue(
TimeTicks::Now(), &transaction_with_task_source.transaction);
if (destination_thread_group == this) {
if (transaction_with_task_source.task_source->immediate_heap_handle()
.IsValid()) {
workers_executor->ScheduleReleaseTaskSource(
std::move(transaction_with_task_source.task_source));
} else {
if (push_to_immediate_queue) {
auto sort_key = transaction_with_task_source.task_source->GetSortKey();
transaction_with_task_source.transaction.Release();
priority_queue_.Push(
std::move(transaction_with_task_source.task_source), sort_key);
}
}
EnsureEnoughWorkersLockRequired(workers_executor);
} else {
reenqueue_executor->SchedulePushTaskSourceAndWakeUpWorkers(
std::move(transaction_with_task_source), destination_thread_group);
}
}
RegisteredTaskSource ThreadGroup::TakeRegisteredTaskSource(
BaseScopedCommandsExecutor* executor) {
DCHECK(!priority_queue_.IsEmpty());
auto run_status = priority_queue_.PeekTaskSource().WillRunTask();
if (run_status == TaskSource::RunStatus::kDisallowed) {
executor->ScheduleReleaseTaskSource(priority_queue_.PopTaskSource());
return nullptr;
}
if (run_status == TaskSource::RunStatus::kAllowedSaturated)
return priority_queue_.PopTaskSource();
RegisteredTaskSource task_source =
task_tracker_->RegisterTaskSource(priority_queue_.PeekTaskSource().get());
if (!task_source)
return priority_queue_.PopTaskSource();
std::swap(priority_queue_.PeekTaskSource(), task_source);
priority_queue_.UpdateSortKey(*task_source.get(), task_source->GetSortKey());
return task_source;
}
void ThreadGroup::UpdateSortKeyImpl(BaseScopedCommandsExecutor* executor,
TaskSource::Transaction transaction) {
CheckedAutoLock auto_lock(lock_);
priority_queue_.UpdateSortKey(*transaction.task_source(),
transaction.task_source()->GetSortKey());
EnsureEnoughWorkersLockRequired(executor);
}
void ThreadGroup::PushTaskSourceAndWakeUpWorkersImpl(
BaseScopedCommandsExecutor* executor,
TransactionWithRegisteredTaskSource transaction_with_task_source) {
CheckedAutoLock auto_lock(lock_);
DCHECK(!replacement_thread_group_);
DCHECK_EQ(delegate_->GetThreadGroupForTraits(
transaction_with_task_source.transaction.traits()),
this);
if (transaction_with_task_source.task_source->immediate_heap_handle()
.IsValid()) {
executor->ScheduleReleaseTaskSource(
std::move(transaction_with_task_source.task_source));
return;
}
auto sort_key = transaction_with_task_source.task_source->GetSortKey();
transaction_with_task_source.transaction.Release();
priority_queue_.Push(std::move(transaction_with_task_source.task_source),
sort_key);
EnsureEnoughWorkersLockRequired(executor);
}
void ThreadGroup::InvalidateAndHandoffAllTaskSourcesToOtherThreadGroup(
ThreadGroup* destination_thread_group) {
CheckedAutoLock current_thread_group_lock(lock_);
CheckedAutoLock destination_thread_group_lock(
destination_thread_group->lock_);
destination_thread_group->priority_queue_ = std::move(priority_queue_);
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;
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());
if (!task_tracker_->CanRunPriority(sort_key.priority()))
return true;
auto max_allowed_sort_key =
TS_UNCHECKED_READ(max_allowed_sort_key_).load(std::memory_order_relaxed);
if (sort_key.priority() > max_allowed_sort_key.priority ||
max_allowed_sort_key.priority == TaskPriority::BEST_EFFORT) {
return false;
}
if (sort_key.priority() == max_allowed_sort_key.priority &&
sort_key.worker_count() <= max_allowed_sort_key.worker_count + 1) {
return false;
}
max_allowed_sort_key =
TS_UNCHECKED_READ(max_allowed_sort_key_)
.exchange(kMaxYieldSortKey, std::memory_order_relaxed);
return max_allowed_sort_key.priority != TaskPriority::BEST_EFFORT;
}
#if BUILDFLAG(IS_WIN)
std::unique_ptr<win::ScopedWindowsThreadEnvironment>
ThreadGroup::GetScopedWindowsThreadEnvironment(WorkerEnvironment environment) {
std::unique_ptr<win::ScopedWindowsThreadEnvironment> scoped_environment;
if (environment == WorkerEnvironment::COM_MTA) {
scoped_environment = std::make_unique<win::ScopedWinrtInitializer>();
}
DCHECK(!scoped_environment || scoped_environment->Succeeded());
return scoped_environment;
}
#endif
bool ThreadGroup::CurrentThreadHasGroup() {
return current_thread_group != nullptr;
}
}
}