blob: 2226b54f80116d2e1207fdb0fcb922eab380234a [file] [log] [blame]
// Copyright 2015 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 "third_party/blink/renderer/platform/scheduler/worker/worker_thread.h"
#include <memory>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/task/sequence_manager/task_queue.h"
#include "base/time/default_tick_clock.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
#include "third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_proxy.h"
#include "third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler.h"
namespace blink {
namespace scheduler {
WorkerThread::WorkerThread(const ThreadCreationParams& params)
: thread_type_(params.thread_type),
worker_scheduler_proxy_(params.frame_or_worker_scheduler
? std::make_unique<WorkerSchedulerProxy>(
params.frame_or_worker_scheduler)
: nullptr),
supports_gc_(params.supports_gc) {
auto non_main_thread_scheduler_factory = base::BindOnce(
&WorkerThread::CreateNonMainThreadScheduler, base::Unretained(this));
base::SimpleThread::Options options;
options.priority = params.thread_priority;
thread_ = std::make_unique<SimpleThreadImpl>(
params.name ? params.name : std::string(), options,
std::move(non_main_thread_scheduler_factory));
if (supports_gc_) {
MemoryPressureListenerRegistry::Instance().RegisterThread(
const_cast<scheduler::WorkerThread*>(this));
}
}
WorkerThread::~WorkerThread() {
if (supports_gc_) {
MemoryPressureListenerRegistry::Instance().UnregisterThread(
const_cast<scheduler::WorkerThread*>(this));
}
thread_->Quit();
thread_->Join();
}
void WorkerThread::Init() {
thread_->StartAsync();
// TODO(carlscab): We could get rid of this if the NonMainThreadSchedulerImpl
// and the default_task_runner could be created on the main thread and then
// bound in the worker thread (similar to what happens with SequenceManager)
thread_->WaitForInit();
}
std::unique_ptr<NonMainThreadSchedulerImpl>
WorkerThread::CreateNonMainThreadScheduler(
base::sequence_manager::SequenceManager* sequence_manager) {
return NonMainThreadSchedulerImpl::Create(thread_type_, sequence_manager,
worker_scheduler_proxy_.get());
}
blink::PlatformThreadId WorkerThread::ThreadId() const {
return thread_->tid();
}
blink::ThreadScheduler* WorkerThread::Scheduler() {
return thread_->GetNonMainThreadScheduler();
}
scoped_refptr<base::SingleThreadTaskRunner> WorkerThread::GetTaskRunner()
const {
return thread_->GetDefaultTaskRunner();
}
WorkerThread::SimpleThreadImpl::SimpleThreadImpl(
const std::string& name_prefix,
const base::SimpleThread ::Options& options,
NonMainThreadSchedulerFactory factory)
: SimpleThread(name_prefix, options),
scheduler_factory_(std::move(factory)) {
// TODO(alexclarke): Do we need to unify virtual time for workers and the main
// thread?
sequence_manager_ = base::sequence_manager::CreateUnboundSequenceManager(
base::sequence_manager::SequenceManager::Settings::Builder()
.SetMessageLoopType(base::MessageLoop::TYPE_DEFAULT)
.SetRandomisedSamplingEnabled(true)
.Build());
internal_task_queue_ = sequence_manager_->CreateTaskQueue(
base::sequence_manager::TaskQueue::Spec("worker_thread_internal_tq"));
internal_task_runner_ = internal_task_queue_->CreateTaskRunner(
base::sequence_manager::kTaskTypeNone);
}
void WorkerThread::SimpleThreadImpl::WaitForInit() {
if (is_initialized_.IsSet())
return;
base::WaitableEvent initialized;
internal_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal,
base::Unretained(&initialized)));
initialized.Wait();
}
void WorkerThread::SimpleThreadImpl::Run() {
auto scoped_sequence_manager = std::move(sequence_manager_);
auto scoped_internal_task_queue = std::move(internal_task_queue_);
scoped_sequence_manager->BindToMessagePump(
base::MessageLoop::CreateMessagePumpForType(
base::MessageLoop::TYPE_DEFAULT));
non_main_thread_scheduler_ =
std::move(scheduler_factory_).Run(scoped_sequence_manager.get());
non_main_thread_scheduler_->Init();
default_task_runner_ =
non_main_thread_scheduler_->DefaultTaskQueue()->CreateTaskRunner(
TaskType::kWorkerThreadTaskQueueDefault);
base::RunLoop run_loop;
run_loop_ = &run_loop;
is_initialized_.Set();
run_loop_->Run();
non_main_thread_scheduler_.reset();
run_loop_ = nullptr;
}
void WorkerThread::SimpleThreadImpl::Quit() {
if (!internal_task_runner_->RunsTasksInCurrentSequence()) {
internal_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&WorkerThread::SimpleThreadImpl::Quit,
base::Unretained(this)));
return;
}
non_main_thread_scheduler_.reset();
// We should only get here if we are called by the run loop.
DCHECK(run_loop_);
run_loop_->Quit();
}
} // namespace scheduler
} // namespace blink