This document provides an overview of task scheduling in Blink and Chrome’s renderer process and outlines best practises for posting tasks.
The majority of the scheduling logic deals explicitly with main thread scheduling and this document assumes that we are talking about the main thread unless stated otherwise. If you don’t need DOM access, please refer to the off main thread scheduling section to find out how to schedule this type of work.
The main scheduling unit in Blink is a task. A task is a
base::OnceClosure posted via
TaskRunner::PostDelayedTask interface. The regular method of creating closures,
base::BindOnce/Repeating, is banned. Blink should use one of the followings:
WTF::Bind: for tasks which are posted to the same thread.
CrossThreadBind: for tasks which are posted to a different thread.
At the moment Blink Scheduler treats tasks as an atomic unit — if a task has started, it can’t be interrupted until it completes. The scheduler can only choose a new task to run from the eligible tasks or can elect not to run any task at all.
In order to post a task, a task runner reference is needed. Almost all main thread tasks should be associated with a frame to allow the scheduler to freeze or prioritise individual frames. This is a hard requirement backed by a
FrameScheduler::GetTaskRunner (or its aliases
ExecutionContext::GetTaskRunner) should be used for that. They return a task runner which continues to run tasks after the frame is detached.
Per-thread task runners include:
New task runners might be added in the future; contact the team if you think you need a new one.
base::SingleThreadTaskRunner::GetCurrentDefault is a way to get a task runner and it returns a default task runner for a thread. Because tasks posted to it lack any attribution, the scheduler can’t properly schedule and prioritise them.
base::SingleThreadTaskRunner::GetCurrentDefault usages are banned in
content/renderer/ directories and strongly discouraged elsewhere in the renderer process.
Please help us to convert them to the appropriate task runner (usually per-frame one). See Task Type Usage Guideline for more details.
In addition to frame association, the scheduler also needs to know the nature of the task to correctly handle it.
blink::TaskType encodes this information.
TaskType is a required parameter of all
GetTaskRunner() methods and
FrameScheduler returns an appropriate task runner based on the
All tasks mentioned in the spec should have task source explicitly defined (e.g. see generic task sources definition in the spec). There are still some places where the task source is not mentioned explicitly — reach out to domenic@ and garykac@ for advice.
kInternal* task types should be used.
If you’re happy with the default scheduling policies, which should happen in the majority of cases,
kInternalDefault task type should be used. Otherwise, reach out to the team to discuss adding new task type for your needs.
The scheduler selects the next task to run based on the priority (modulo some starvation logic). The tasks with the same priority run in order.
There are following rules to assign priorities:
The default priority is normal.
During synchronous dialogs (
The pausing is triggered by
ScopedPagePauser. Almost all tasks can be paused.
Scheduler may elect not to run some tasks when processing user gestures in order to increase responsiveness.
Scheduler defers tasks for two seconds after a user gesture, as it’s very likely that another gesture will arrive soon. The majority of tasks can be deferred.
Most of the tasks are deferrable.
Scheduler freezes background pages in order to increase responsiveness of the foreground tabs and save power.
On mobile all pages are frozen after five minutes in the background. On desktop only eligible pages are frozen, which is determined by heuristics based on the APIs page is using.
Most of the tasks are freezable.
Scheduler delays tasks in the background pages and offscreen frames in order to improve responsiveness of the foreground pages without breaking useful background page functionality.
setInterval) are throttleable. While there is a general desire to expand this list, this is a low priority effort as we are focused on making freezing better instead.
If your task doesn’t have to run on the main thread, use
worker_pool::PostTask, which uses a thread pool behind the scenes.
Do not create your own dedicated thread if you need ordering for your tasks, use
worker_pool::CreateTaskRunner instead — this creates a sequence (virtual thread which can run tasks in order on any of the threads in the thread pool).
See also Threading and Tasks in Chrome for more details.
Many data structures in Blink are bound to a particular thread (e.g. Strings, garbage-collected classes, etc), so it’s not safe to pass a pointer to them to another thread. To enforce this,
base::Bind is banned in Blink and
CrossThreadBind are provided as alternatives.
WTF::Bind should be used to post tasks to the same thread and closures returned by it DCHECK that they run on the same thread.
CrossThreadCopier to its arguments and creates a deep copy, so the resulting closure can run on a different thread.