0
Files
android_webview
apps
ash
base
build
build_overrides
buildtools
cc
chrome
chromecast
chromeos
clank
codelabs
components
content
crypto
dbus
device
docs
accessibility
autofill
chromeos
design
enterprise
experiments
fuchsia
gpu
graphics
images
infra
intl
ios
linux
login
mac
media
memory
memory-infra
patterns
privacy
privacy_budget
process
security
speed
speed_metrics
standards
telemetry_extension
testing
transcripts
ui
updater
webapps
website
webui
workflow
DIR_METADATA
OWNERS
README.md
accessibility.md
ad_tagging.md
adding_to_third_party.md
android_accessing_cpp_enums_in_java.md
android_accessing_cpp_features_in_java.md
android_accessing_cpp_switches_in_java.md
android_build_instructions.md
android_cast_build_instructions.md
android_debugging_instructions.md
android_dynamic_feature_modules.md
android_emulator.md
android_isolated_splits.md
android_jni_ownership_best_practices.md
android_logging.md
android_native_libraries.md
android_studio.md
angle_in_chromium.md
api_keys.md
asan.md
atom.md
benchmark_performance_regressions.md
bfcache.md
bitmap_pipeline.md
branch_gardener.md
building_old_revisions.md
callback.md
ccache_mac.md
chrome_browser_design_principles.md
chrome_os_logging.md
chrome_settings.md
chromedriver_status.md
chromeos_build_instructions.md
chromeos_glossary.md
chromium_browser_vs_google_chrome.md
cipd_and_3pp.md
cl_respect.md
cl_tips.md
clang.md
clang_code_coverage_wrapper.md
clang_format.md
clang_gardening.md
clang_sheriffing.md
clang_static_analyzer.md
clang_tidy.md
clang_tool_refactoring.md
clangd.md
clion.md
closure_compilation.md
cocoa_tips_and_tricks.md
code_review_owners.md
code_reviews.md
commit_checklist.md
component_build.md
configuration.md
contributing.md
cq_fault_attribution.md
cr_respect.md
cr_user_manual.md
cross_platform_ui.md
cygwin_dll_remapping_failure.md
dangling_ptr.md
dangling_ptr_guide.md
dbus_mojo_connection_service.md
debugging_with_crash_keys.md
dependencies.md
deterministic_builds.md
disassemble_code.md
documentation_best_practices.md
documentation_guidelines.md
early-hints.md
eclipse.md
emacs.md
erc_irc.md
flag_expiry.md
flag_guarding_guidelines.md
flag_ownership.md
frame_trees.md
gardener.md
gcs_dependencies.md
gdbinit.md
get_the_code.md
git_cookbook.md
git_submodules.md
git_tips.md
google_chrome_branded_builds.md
google_play_services.md
graphical_debugging_aid_chromium_views.md
gwp_asan.md
history_manipulation_intervention.md
how_cc_works.md
how_to_add_your_feature_flag.md
how_to_extend_web_test_framework.md
idn.md
initialize_blink_features.md
inlined_stack_traces.md
installation_at_vmware.md
ios_build_instructions.md
ios_infra.md
ios_voiceover.md
kiosk_mode.md
life_of_a_frame.md
lldbinit.md
mac_arm64.md
mac_build_instructions.md
mac_lld.md
modifying_session_history_serialization.md
modules.md
mojo_and_services.md
mojo_ipc_conversion.md
mojo_testing.md
native_relocations.md
navbar.md
navigation-request-navigation-state.gv
navigation-request-navigation-state.png
navigation.md
navigation_concepts.md
network_traffic_annotations.md
no_sources_assignment_filter.md
orderfile.md
origin_trials_integration.md
ozone_overview.md
parsing_test_results.md
pgo.md
piranha_plant.md
process_model_and_site_isolation.md
profiling.md
profiling_content_shell_on_android.md
proxy_auto_config.md
qtcreator.md
release_branch_guidance.md
render-frame-host-lifecycle-state.gv
render-frame-host-lifecycle-state.png
render_document.md
rust-unsafe.md
rust.md
seccomp_sandbox_crash_dumping.md
servicification.md
session_history.md
sheriff.md
shutdown.md
special_case_urls.md
static_initializers.md
sublime_ide.md
system_hardening_features.md
tab_helpers.md
threading_and_tasks.md
threading_and_tasks_faq.md
threading_and_tasks_testing.md
toolchain_support.md
tour_of_luci_ui.md
tpm_quick_ref.md
translation_screenshots.md
unretained_dangling_ptr_guide.md
unsafe_buffers.md
updating_clang.md
updating_clang_format_binaries.md
use_counter_wiki.md
useful_urls.md
user_data_dir.md
user_data_storage.md
user_handle_mapping.md
vanilla_msysgit_workflow.md
vscode.md
vscode_python.md
webview_policies.md
win_cross.md
win_order_files.md
windows_build_instructions.md
windows_native_window_occlusion_tracking.md
windows_pwa_integration.md
windows_shortcut_and_taskbar_handling.md
windows_split_dll.md
windows_virtual_desktop_handling.md
wmax_tokens.md
working_remotely_with_android.md
writing_clang_plugins.md
extensions
fuchsia_web
gin
google_apis
gpu
headless
infra
internal
ios
ios_internal
ipc
media
mojo
native_client
native_client_sdk
net
pdf
ppapi
printing
remoting
rlz
sandbox
services
signing_keys
skia
sql
storage
styleguide
testing
third_party
tools
ui
url
v8
webkit
.clang-format
.clang-tidy
.clangd
.git-blame-ignore-revs
.gitallowed
.gitattributes
.gitignore
.gitmodules
.gn
.mailmap
.rustfmt.toml
.vpython3
.yapfignore
ATL_OWNERS
AUTHORS
BUILD.gn
CODE_OF_CONDUCT.md
CPPLINT.cfg
CRYPTO_OWNERS
DEPS
DIR_METADATA
LICENSE
LICENSE.chromium_os
OWNERS
PRESUBMIT.py
PRESUBMIT_test.py
PRESUBMIT_test_mocks.py
README.md
WATCHLISTS
codereview.settings
src/docs/threading_and_tasks_faq.md
Sean Maher 70f2942930 Task posting v3: many manual changes to continue handle refactors
This CL contains:
- removal of references to task runner handles in comments
- removal of nearly all calls, includes, and instantiations of
  thread and sequenced task runner handles

Each of these were replaced with references to the new api:
TaskRunner::CurrentDefaultHandle and associated methods.

After this change, all that should be left to do for this method
refactor is the deletion of the old API.

A few of the changes were done with a script, but they were manually
audited.

Bug: 1026641
Ax-Relnotes: n/a
Change-Id: I0858dccac95b485d982aa5152c6b461c4ce05aa4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4117257
Commit-Queue: Gabriel Charette <gab@chromium.org>
Owners-Override: Gabriel Charette <gab@chromium.org>
Reviewed-by: Gabriel Charette <gab@chromium.org>
Commit-Queue: Sean Maher <spvw@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1088971}
2023-01-04 22:15:06 +00:00

8.7 KiB

Threading and Tasks in Chrome - FAQ

[TOC]

Note: Make sure to read the main Threading and Tasks docs first.

General

On which thread will a task run?

A task is posted through the base/task/thread_pool.h API with TaskTraits.

  • If TaskTraits contain BrowserThread::UI:

    • The task runs on the main thread.
  • If TaskTraits contain BrowserThread::IO:

    • The task runs on the IO thread.
  • If TaskTraits don't contain BrowserThread::UI/IO:

    • If the task is posted through a SingleThreadTaskRunner obtained from CreateSingleThreadTaskRunner(..., mode):

      • Where mode is SingleThreadTaskRunnerThreadMode::DEDICATED:

        • The task runs on a thread that only runs tasks from that SingleThreadTaskRunner. This is not the main thread nor the IO thread.
      • Where mode is SingleThreadTaskRunnerThreadMode::SHARED:

        • The task runs on a thread that runs tasks from one or many unrelated SingleThreadTaskRunners. This is not the main thread nor the IO thread.
    • Otherwise:

      • The task runs in a thread pool.

As explained in Prefer Sequences to Threads, tasks should generally run on a sequence in a thread pool rather than on a dedicated thread.

Does release of a TaskRunner block on posted tasks?

Releasing a TaskRunner reference does not wait for tasks previously posted to the TaskRunner to complete their execution. Tasks can run normally after the last client reference to the TaskRunner to which they were posted has been released and it can even be kept alive indefinitely through SequencedTaskRunner::GetCurrentDefault() or SingleThreadTaskRunner::GetCurrentDefault().

If you want some state to be deleted only after all tasks currently posted to a SequencedTaskRunner have run, store that state in a helper object and schedule deletion of that helper object on the SequencedTaskRunner using base::OnTaskRunnerDeleter after posting the last task. See example CL. But be aware that any task posting back to its "current" sequence can enqueue itself after that "last" task.

Making blocking calls (which do not use the CPU)

How to make a blocking call without preventing other tasks from being scheduled?

The steps depend on where the task runs (see Where will a task run?).

If the task runs in a thread pool:

  • Annotate the scope that may block with ScopedBlockingCall(BlockingType::MAY_BLOCK/WILL_BLOCK). A few milliseconds after the annotated scope is entered, the capacity of the thread pool is incremented. This ensures that your task doesn't reduce the number of tasks that can run concurrently on the CPU. If the scope exits, the thread pool capacity goes back to normal.

If the task runs on the main thread, the IO thread or a SHARED SingleThreadTaskRunner:

  • Blocking on one of these threads will cause breakages. Move your task to a thread pool (or to a DEDICATED SingleThreadTaskRunner if necessary - see Prefer Sequences to Threads).

If the task runs on a DEDICATED SingleThreadTaskRunner:

  • Annotate the scope that may block with ScopedBlockingCall(BlockingType::MAY_BLOCK/WILL_BLOCK). The annotation is a no-op that documents the blocking behavior (and makes it pass assertions). Tasks posted to the same DEDICATED SingleThreadTaskRunner won't run until your blocking task returns (they will never run if the blocking task never returns).

base/threading/scoped_blocking_call.h explains the difference between MAY_BLOCK and WILL_BLOCK and gives examples of blocking operations.

How to make a blocking call that may never return without preventing other tasks from being scheduled?

If you can't avoid making a call to a third-party library that may block off- CPU, follow recommendations in How to make a blocking call without affecting other tasks?. This ensures that a current task doesn't prevent other tasks from running even if it never returns.

Since tasks posted to the same sequence can't run concurrently, it is advisable to run tasks that may block indefinitely in parallel rather than in sequence (unless posting many such tasks at which point sequencing can be a useful tool to prevent flooding).

Do calls to blocking //base APIs need to be annotated with ScopedBlockingCall?

No. All blocking //base APIs (e.g. base::ReadFileToString, base::File::Read, base::SysInfo::AmountOfFreeDiskSpace, base::WaitableEvent::Wait, etc.) have their own internal annotations. See base/threading/scoped_blocking_call.h.

Can multiple ScopedBlockingCall be nested for the purpose of documentation?

Nested ScopedBlockingCall are supported. Most of the time, the inner ScopedBlockingCalls will no-op (the exception is WILL_BLOCK nested in MAY_BLOCK). As such, it is permitted to add a ScopedBlockingCall in the scope where a function that is already annotated is called for documentation purposes.:

Data GetDataFromNetwork() {
  ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
  // Fetch data from network.
  ...
  return data;
}

void ProcessDataFromNetwork() {
  Data data;
  {
    // Document the blocking behavior with a ScopedBlockingCall.
    // Permitted, but not required since GetDataFromNetwork() is itself annotated.
    ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK);
    data = GetDataFromNetwork();
  }
  CPUIntensiveProcessing(data);
}

However, CPU usage should always be minimal within the scope of ScopedBlockingCall. See base/threading/scoped_blocking_call.h.

Sequences

How to migrate from SingleThreadTaskRunner to SequencedTaskRunner?

The following mappings can be useful when migrating code from a SingleThreadTaskRunner to a SequencedTaskRunner:

  • base::SingleThreadTaskRunner -> base::SequencedTaskRunner
    • SingleThreadTaskRunner::BelongsToCurrentThread() -> SequencedTaskRunner::RunsTasksInCurrentSequence()
  • base::SingleThreadTaskRunner::CurrentDefaultHandle -> base::SequencedTaskRunnerHandle::CurrentDefaultHandle
  • THREAD_CHECKER -> SEQUENCE_CHECKER
  • base::ThreadLocalStorage::Slot -> base::SequenceLocalStorageSlot
  • BrowserThread::DeleteOnThread -> base::OnTaskRunnerDeleter / base::RefCountedDeleteOnSequence
  • BrowserMessageFilter::OverrideThreadForMessage() -> BrowserMessageFilter::OverrideTaskRunnerForMessage()
  • CreateSingleThreadTaskRunner() -> CreateSequencedTaskRunner()
    • Every CreateSingleThreadTaskRunner() usage, outside of BrowserThread::UI/IO, should be accompanied with a comment and ideally a bug to make it sequence when the sequence-unfriendly dependency is addressed.

How to ensure mutual exclusion between tasks posted by a component?

Create a SequencedTaskRunner using CreateSequencedTaskRunner() and store it on an object that can be accessed from all the PostTask() call sites that require mutual exclusion. If there isn't a shared object that can own a common SequencedTaskRunner, use Lazy(Sequenced|SingleThread|COMSTA)TaskRunner in an anonymous namespace.

Tests

How to test code that posts tasks?

If the test uses BrowserThread::UI/IO, instantiate a content::BrowserTaskEnvironment for the scope of the test. Call BrowserTaskEnvironment::RunUntilIdle() to wait until all tasks have run.

If the test doesn't use BrowserThread::UI/IO, instantiate a base::test::TaskEnvironment for the scope of the test. Call base::test::TaskEnvironment::RunUntilIdle() to wait until all tasks have run.

In both cases, you can run tasks until a condition is met. A test that waits for a condition to be met is easier to understand and debug than a test that waits for all tasks to run.

int g_condition = false;

base::RunLoop run_loop;
base::ThreadPool::PostTask(FROM_HERE, {}, base::BindOnce(
    [] (base::OnceClosure closure) {
        g_condition = true;
        std::move(quit_closure).Run();
    }, run_loop.QuitClosure()));

// Runs tasks until the quit closure is invoked.
run_loop.Run();

EXPECT_TRUE(g_condition);

Your question hasn't been answered?

  1. Check the main Threading and Tasks docs.
  2. Ping scheduler-dev@chromium.org.