blob: c4d20ba6c4525653067076acc45b5b8038f2eeb7 [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/check.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/message_loop/message_pump.h"
#include "base/message_loop/message_pump_type.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/heap/blink_gc_memory_dump_provider.h"
#include "third_party/blink/renderer/platform/instrumentation/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) {
base::SimpleThread::Options options;
options.priority = params.thread_priority;
thread_ = std::make_unique<SimpleThreadImpl>(
params.name ? params.name : String(), options, supports_gc_,
const_cast<scheduler::WorkerThread*>(this));
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();
base::ScopedAllowBaseSyncPrimitives allow_wait;
thread_->Join();
}
void WorkerThread::Init() {
thread_->CreateScheduler();
thread_->StartAsync();
}
std::unique_ptr<NonMainThreadSchedulerImpl>
WorkerThread::CreateNonMainThreadScheduler(
base::sequence_manager::SequenceManager* sequence_manager) {
return NonMainThreadSchedulerImpl::Create(thread_type_, sequence_manager,
worker_scheduler_proxy_.get());
}
blink::ThreadScheduler* WorkerThread::Scheduler() {
return thread_->GetNonMainThreadScheduler();
}
scoped_refptr<base::SingleThreadTaskRunner> WorkerThread::GetTaskRunner()
const {
return thread_->GetDefaultTaskRunner();
}
void WorkerThread::ShutdownOnThread() {
thread_->ShutdownOnThread();
Scheduler()->Shutdown();
}
WorkerThread::SimpleThreadImpl::SimpleThreadImpl(
const WTF::String& name_prefix,
const base::SimpleThread ::Options& options,
bool supports_gc,
WorkerThread* worker_thread)
: SimpleThread(name_prefix.Utf8(), options),
thread_(worker_thread),
supports_gc_(supports_gc) {
// 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()
.SetMessagePumpType(base::MessagePumpType::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::CreateScheduler() {
DCHECK(!non_main_thread_scheduler_);
DCHECK(!default_task_runner_);
DCHECK(sequence_manager_);
non_main_thread_scheduler_ =
thread_->CreateNonMainThreadScheduler(sequence_manager_.get());
non_main_thread_scheduler_->Init();
default_task_runner_ =
non_main_thread_scheduler_->DefaultTaskQueue()->CreateTaskRunner(
TaskType::kWorkerThreadTaskQueueDefault);
}
WorkerThread::GCSupport::GCSupport(WorkerThread* thread) {
ThreadState* thread_state = ThreadState::AttachCurrentThread();
gc_task_runner_ = std::make_unique<GCTaskRunner>(thread);
blink_gc_memory_dump_provider_ = std::make_unique<BlinkGCMemoryDumpProvider>(
thread_state, base::ThreadTaskRunnerHandle::Get(),
BlinkGCMemoryDumpProvider::HeapType::kBlinkWorkerThread);
}
WorkerThread::GCSupport::~GCSupport() {
// Ensure no posted tasks will run from this point on.
gc_task_runner_.reset();
blink_gc_memory_dump_provider_.reset();
ThreadState::DetachCurrentThread();
}
void WorkerThread::SimpleThreadImpl::ShutdownOnThread() {
gc_support_.reset();
}
void WorkerThread::SimpleThreadImpl::Run() {
DCHECK(non_main_thread_scheduler_)
<< "CreateScheduler() should be called before starting the thread.";
non_main_thread_scheduler_->AttachToCurrentThread();
auto scoped_sequence_manager = std::move(sequence_manager_);
auto scoped_internal_task_queue = std::move(internal_task_queue_);
scoped_sequence_manager->BindToMessagePump(
base::MessagePump::Create(base::MessagePumpType::DEFAULT));
base::RunLoop run_loop;
run_loop_ = &run_loop;
Thread::UpdateThreadTLS(thread_);
if (supports_gc_)
gc_support_ = std::make_unique<GCSupport>(thread_);
run_loop_->Run();
gc_support_.reset();
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;
}
// We should only get here if we are called by the run loop.
DCHECK(run_loop_);
run_loop_->Quit();
}
} // namespace scheduler
} // namespace blink