blob: e46a5b42977288fe6f9afb1c4c1b0d78c442473c [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/task_scheduler/task_scheduler_impl.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/task_scheduler/scheduler_service_thread.h"
#include "base/task_scheduler/scheduler_thread_pool_impl.h"
#include "base/task_scheduler/sequence_sort_key.h"
#include "base/task_scheduler/task.h"
#include "base/time/time.h"
namespace base {
namespace internal {
// static
std::unique_ptr<TaskSchedulerImpl> TaskSchedulerImpl::Create() {
std::unique_ptr<TaskSchedulerImpl> scheduler(new TaskSchedulerImpl);
scheduler->Initialize();
return scheduler;
}
TaskSchedulerImpl::~TaskSchedulerImpl() {
#if DCHECK_IS_ON()
DCHECK(join_for_testing_returned_.IsSignaled());
#endif
}
void TaskSchedulerImpl::PostTaskWithTraits(
const tracked_objects::Location& from_here,
const TaskTraits& traits,
const Closure& task) {
// Post |task| as part of a one-off single-task Sequence.
GetThreadPoolForTraits(traits)->PostTaskWithSequence(
WrapUnique(new Task(from_here, task, traits, TimeDelta())),
make_scoped_refptr(new Sequence), nullptr);
}
scoped_refptr<TaskRunner> TaskSchedulerImpl::CreateTaskRunnerWithTraits(
const TaskTraits& traits,
ExecutionMode execution_mode) {
return GetThreadPoolForTraits(traits)->CreateTaskRunnerWithTraits(
traits, execution_mode);
}
void TaskSchedulerImpl::Shutdown() {
// TODO(fdoray): Increase the priority of BACKGROUND tasks blocking shutdown.
task_tracker_.Shutdown();
}
void TaskSchedulerImpl::JoinForTesting() {
#if DCHECK_IS_ON()
DCHECK(!join_for_testing_returned_.IsSignaled());
#endif
background_thread_pool_->JoinForTesting();
background_file_io_thread_pool_->JoinForTesting();
normal_thread_pool_->JoinForTesting();
normal_file_io_thread_pool_->JoinForTesting();
service_thread_->JoinForTesting();
#if DCHECK_IS_ON()
join_for_testing_returned_.Signal();
#endif
}
TaskSchedulerImpl::TaskSchedulerImpl()
: delayed_task_manager_(
Bind(&TaskSchedulerImpl::OnDelayedRunTimeUpdated, Unretained(this)))
#if DCHECK_IS_ON()
,
join_for_testing_returned_(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED)
#endif
{
}
void TaskSchedulerImpl::Initialize() {
using IORestriction = SchedulerThreadPoolImpl::IORestriction;
const SchedulerThreadPoolImpl::ReEnqueueSequenceCallback
re_enqueue_sequence_callback =
Bind(&TaskSchedulerImpl::ReEnqueueSequenceCallback, Unretained(this));
// TODO(fdoray): Derive the number of threads per pool from hardware
// characteristics rather than using hard-coded constants.
// Passing pointers to objects owned by |this| to
// SchedulerThreadPoolImpl::Create() is safe because a TaskSchedulerImpl can't
// be deleted before all its thread pools have been joined.
background_thread_pool_ = SchedulerThreadPoolImpl::Create(
"TaskSchedulerBackground", ThreadPriority::BACKGROUND, 1U,
IORestriction::DISALLOWED, re_enqueue_sequence_callback, &task_tracker_,
&delayed_task_manager_);
CHECK(background_thread_pool_);
background_file_io_thread_pool_ = SchedulerThreadPoolImpl::Create(
"TaskSchedulerBackgroundFileIO", ThreadPriority::BACKGROUND, 1U,
IORestriction::ALLOWED, re_enqueue_sequence_callback, &task_tracker_,
&delayed_task_manager_);
CHECK(background_file_io_thread_pool_);
normal_thread_pool_ = SchedulerThreadPoolImpl::Create(
"TaskSchedulerForeground", ThreadPriority::NORMAL, 4U,
IORestriction::DISALLOWED, re_enqueue_sequence_callback, &task_tracker_,
&delayed_task_manager_);
CHECK(normal_thread_pool_);
normal_file_io_thread_pool_ = SchedulerThreadPoolImpl::Create(
"TaskSchedulerForegroundFileIO", ThreadPriority::NORMAL, 12U,
IORestriction::ALLOWED, re_enqueue_sequence_callback, &task_tracker_,
&delayed_task_manager_);
CHECK(normal_file_io_thread_pool_);
service_thread_ = SchedulerServiceThread::Create(&task_tracker_,
&delayed_task_manager_);
CHECK(service_thread_);
}
SchedulerThreadPool* TaskSchedulerImpl::GetThreadPoolForTraits(
const TaskTraits& traits) {
if (traits.with_file_io()) {
if (traits.priority() == TaskPriority::BACKGROUND)
return background_file_io_thread_pool_.get();
return normal_file_io_thread_pool_.get();
}
if (traits.priority() == TaskPriority::BACKGROUND)
return background_thread_pool_.get();
return normal_thread_pool_.get();
}
void TaskSchedulerImpl::ReEnqueueSequenceCallback(
scoped_refptr<Sequence> sequence) {
DCHECK(sequence);
const SequenceSortKey sort_key = sequence->GetSortKey();
TaskTraits traits(sequence->PeekTask()->traits);
// Update the priority of |traits| so that the next task in |sequence| runs
// with the highest priority in |sequence| as opposed to the next task's
// specific priority.
traits.WithPriority(sort_key.priority());
GetThreadPoolForTraits(traits)->ReEnqueueSequence(std::move(sequence),
sort_key);
}
void TaskSchedulerImpl::OnDelayedRunTimeUpdated() {
service_thread_->WakeUp();
}
} // namespace internal
} // namespace base