Commit 7e9af624 authored by Hiroki Nakagawa's avatar Hiroki Nakagawa Committed by Commit Bot

Worker: Capture worker task runners on worker scheduler initialization

Before this CL, WorkerThread::GetTaskRunner() took a task runner from
the worker scheduler on demand. The worker scheduler is disposed of
during worker termination, so GetTaskRunner() must not be called after
worker termination starts. Unfortunately it is difficult to enforce the
rule everywhere, and this caused issues like https://crbug.com/1104046.

To fix this, this CL captures all task runners on worker scheduler
initialization, and WorkerThread::GetTaskRunner() return the captured
task runner instead of accessing the worker scheduler to create a new
task runner.

Bug: 1112191
Change-Id: Ic39a515272e77fc3fb2c94ae842a89b15a78466c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2342236Reviewed-by: default avatarKenichi Ishibashi <bashi@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Commit-Queue: Hiroki Nakagawa <nhiroki@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799009}
parent c822762c
......@@ -440,6 +440,14 @@ scheduler::WorkerScheduler* WorkerThread::GetScheduler() {
return worker_scheduler_.get();
}
scoped_refptr<base::SingleThreadTaskRunner> WorkerThread::GetTaskRunner(
TaskType type) {
// Task runners must be captured when the worker scheduler is initialized. See
// comments in InitializeSchedulerOnWorkerThread().
CHECK(worker_task_runners_.Contains(type)) << static_cast<int>(type);
return worker_task_runners_.at(type);
}
void WorkerThread::ChildThreadStartedOnWorkerThread(WorkerThread* child) {
DCHECK(IsCurrentThread());
#if DCHECK_IS_ON()
......@@ -545,6 +553,46 @@ void WorkerThread::InitializeSchedulerOnWorkerThread(
static_cast<scheduler::WorkerThreadScheduler*>(
worker_thread.GetNonMainThreadScheduler()),
worker_thread.worker_scheduler_proxy());
// Capture the worker task runners so that it's safe to access GetTaskRunner()
// from any threads even after the worker scheduler is disposed of on the
// worker thread. See also comments on GetTaskRunner().
// We only capture task types that are actually used. When you want to use a
// new task type, add it here.
Vector<TaskType> available_task_types = {TaskType::kBackgroundFetch,
TaskType::kCanvasBlobSerialization,
TaskType::kDatabaseAccess,
TaskType::kDOMManipulation,
TaskType::kFileReading,
TaskType::kFontLoading,
TaskType::kInternalDefault,
TaskType::kInternalInspector,
TaskType::kInternalLoading,
TaskType::kInternalMedia,
TaskType::kInternalMediaRealTime,
TaskType::kInternalTest,
TaskType::kInternalWebCrypto,
TaskType::kJavascriptTimerDelayed,
TaskType::kJavascriptTimerImmediate,
TaskType::kMediaElementEvent,
TaskType::kMicrotask,
TaskType::kMiscPlatformAPI,
TaskType::kNetworking,
TaskType::kPerformanceTimeline,
TaskType::kPermission,
TaskType::kPostedMessage,
TaskType::kRemoteEvent,
TaskType::kUserInteraction,
TaskType::kWebGL,
TaskType::kWebLocks,
TaskType::kWebSocket,
TaskType::kWorkerAnimation};
for (auto type : available_task_types) {
auto task_runner = worker_scheduler_->GetTaskRunner(type);
auto result = worker_task_runners_.insert(type, std::move(task_runner));
DCHECK(result.is_new_entry);
}
waitable_event->Signal();
}
......
......@@ -231,16 +231,11 @@ class CORE_EXPORT WorkerThread : public Thread::TaskObserver {
scheduler::WorkerScheduler* GetScheduler();
// Returns a task runner bound to the per-global-scope scheduler's task queue.
// You don't have to care about the lifetime of the associated global scope
// and underlying thread. After the global scope is destroyed, queued tasks
// are discarded and PostTask on the returned task runner just fails. This
// function can be called on both the main thread and the worker thread.
// You must not call this after Terminate() is called.
scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType type) {
DCHECK(worker_scheduler_);
return worker_scheduler_->GetTaskRunner(type);
}
// Returns a task runner bound to this worker. Users of the task runner don't
// have to care about the lifetime of the worker. When the worker global scope
// is destroyed, the task runner starts failing PostTask calls and discards
// queued tasks. This function can be called from any threads.
scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType type);
void ChildThreadStartedOnWorkerThread(WorkerThread*);
void ChildThreadTerminatedOnWorkerThread(WorkerThread*);
......@@ -439,6 +434,14 @@ class CORE_EXPORT WorkerThread : public Thread::TaskObserver {
// closed.
std::unique_ptr<scheduler::WorkerScheduler> worker_scheduler_;
// Task runners bound with |worker_scheduler_|. These are captured when the
// worker scheduler is initialized.
using TaskRunnerHashMap = HashMap<TaskType,
scoped_refptr<base::SingleThreadTaskRunner>,
WTF::IntHash<TaskType>,
TaskTypeTraits>;
TaskRunnerHashMap worker_task_runners_;
// This lock protects shared states between the main thread and the worker
// thread. See thread-safety annotations (e.g., GUARDED_BY) in this header
// file.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment