Moment For Technology

Chrome multithreaded task handling

Posted on Aug. 8, 2022, 8:58 a.m. by 解琬婷
Category: The code of life Tag: chrome

Chrome multithreaded task handling

Threads and Tasks

Chrome has a multi-process architecture, and each process has a large number of threads. The basic threading system shared by each process. The main goal is to keep the main thread (also known as the "UI" thread in the browser process) and the IO thread (the thread used for each process that processes IPC) responsive. This means allocating any blocked I/O or other expensive operations to other threads. This is achieved by using messaging as a means of communication between threads. Locking and thread-safe objects are not recommended. Instead, objects live on only one (usually virtual) thread and communicate between these threads by passing messages.

The core concept

  • Tasks: Unit of work to be processed. A function pointer that effectively has an optional state. In Chrome, this isbase::Callbackthroughbase::BindThe document) created by.
  • Task Queue: A queue of tasks to be processed.
  • Physical threads: Threads provided by the operating system (such as pThread on POSIX or CreateThread () on Windows). Chrome cross-platform abstraction forbase::PlatformThread. It should almost never be used directly.
  • base::Thread: The physical thread always processes messages from the dedicated task queue until Quit (). You should almost never create yourselfbase::Thread.
  • Thread pool: a physical thread pool with a shared task queue. In Chrome, this isbase::ThreadPoolInstance. Each Chrome process has only one instance, and it can handle the tasks published through itbase/task/post_task.hSo you hardly need onebase::ThreadPoolInstanceUse the API directly (more on tasks to be released later).
  • Sequence or Virtual thread: Execution threads managed by Chrome. Like physical threads, only one task can run on a given sequence/virtual thread at any given time, and each task can see the side effects of previous tasks. Tasks are executed sequentially, but physical threads may jump between each task.
  • Task Runner: The interface through which tasks can be published. In Chrome, it isbase::TaskRunner.
  • Sequenced Task Runner: a task runner that guarantees that tasks published to it will run in the order they are published. Ensure that each such task sees the side effects of its predecessor. Tasks published to the sorted task runner are typically processed by a single thread (virtual or physical). In Chrome, this isbase::SequencedTaskRunner-a base::TaskRunner.
  • Single-thread Task RunnetSequential tasks run the program to ensure that all tasks will be processed by the same physical thread. In Chrome, this isbase::SingleThreadTaskRunner-a base::SequencedTaskRunner. Whenever possible,Use sequences rather than threads whenever possible.

Thread dictionary

Note: The following terms are intended to bridge the gap between common thread nomenclature and the way they are used in Chrome. If this is difficult to parse, consider skipping to the more detailed section below and referring to it if necessary.

  • (thread-unsafe): Most types in Chrome are thread-unsafe (by design). Access to such types or methods must be externally synchronized. Typically, thread-unsafe types require that all tasks accessing their state be posted to the same task,base::SequencedTaskRunnerAnd in theSEQUENCE_CHECKERThis is verified in the debug version of the Locking is also a method of synchronous access, but in Chrome, we stronglyThe recommended sequenceRather thanThe lock.
  • Thread affineThis type or method always needs to be sent from the same physical thread (i.e., frombase::SingleThreadTaskRunner), and usually has oneTHREAD_CHECKERMember to verify that they are correct. Lack of use of third-party apis or leaf dependencies (thread affine) : There is little reason in Chrome for a type to be thread affine. Pay attention tobase::SingleThreadTaskRunnerIs a,base::SequencedTaskRunnerSo thread affine is an unsafe subset of threads. Thread affine is sometimes calledThe thread of malicious.
  • Thread-safe: It is safe to access these types or methods simultaneously.
  • Thread-compatible: This class provides secure concurrent access to const methods, but requires synchronization of nonconst (or a mixture of const/nonconst) access. Chrome does not expose reader locks; Thus, the only use cases are objects (usually global objects) that are lazily initialized in a thread-safe manner (during the single-thread phase of initiation or through thread-safe static local localization paradigm)base::NoDestructor) and never change.
  • Immutable: A subset of thread-compatible types that cannot be modified after construction.
  • Sequence-friendly: This class or method is a thread-unsafe type and can be called from itbase::SequencedTaskRunner. Ideally, this would be the case for all thread-unsafe types, but older versions of the code sometimes overcheck and enforce thread affinity only when thread-safe. For more details, see"Priority thread".

The number of threads

Every Chrome process has it

  • The main thread
    • In the browser process (BrowserThread :: UI) : Updates the UI
    • In the renderer process (Blink main thread) : Run most blinks
  • IO thread
    • In the browser process (BrowserThread :: IO) : handles IPC and network requests
    • In the renderer process: Process IPC
  • Some dedicated threads
  • And common thread pools

Most threads have a loop that takes tasks from a queue and runs them (the queue can be shared between multiple threads).


The task is added to the queue by Base ::OnceClosure for asynchronous execution.

A base::OnceClosure stores function Pointers and arguments. It has methods that Run() calls a function pointer with bound arguments. It is created using base::BindOnce. See the Callback and Bind () documentation.

void TaskA() {}
void TaskB(int v) {}

auto task_a = base::BindOnce(TaskA);
auto task_b = base::BindOnce(TaskB, 42);
Copy the code

A set of tasks can be performed in one of the following ways:

  • Parallel: There is no order in which tasks are executed and they may all be executed at once on any thread
  • Sequenced: Tasks executed in release order, once on any thread.
  • Single Threaded: Tasks executed in release order, one thread at a time.
  • COM Single Threaded: variant of COM initialization with Single threads.

Prioritize sequences over physical threads

Sequential execution (on virtual threads) is preferred over single-threaded execution (on physical threads). In addition to bind to the main thread (UI) or the type of IO thread or method, base: : SequencedTaskRunner by managing their own physical thread than by managing their own physical thread a better thread safe release (please see the following serialization task).

All apis exposed to the current physical thread have the equivalent of the current sequence (mapping).

If you find yourself writing a sequence-friendly type and THREAD_CHECKER fails a thread affinity check in a leaf dependency (for example), consider making that dependency sequence-friendly as well. Most of the core apis in Chrome are order friendly, but some traditional types may still over-use ThreadChecker/ThreadTaskRunnerHandle/SingleThreadTaskRunner instead of relying on the "current sequence", Instead of affine.

Publishing parallel Tasks

Publish directly to the thread pool

Tasks that can run on any thread and are not ordered or mutually exclusive with other tasks should publish base/task/post_task.h using one of the functions defined in Base ::PostTask*().

base::PostTask(FROM_HERE, base::BindOnce(Task));
Copy the code

This will publish tasks with default characteristics.

The base::PostTask*() function allows callers to provide additional details about tasks through TaskTraits (see Annotating tasks with TaskTraits).

    FROM_HERE, {base::TaskPriority::BEST_EFFORT, MayBlock()},
Copy the code

Posted via TaskRunner

Parallel Base ::TaskRunner is an alternative to a direct call to Base ::PostTask*(). This is especially useful when you don't know in advance whether tasks will be published in parallel, sequentially, or single-threaded (see Publishing serialized Tasks, publishing multiple tasks to the same thread). Because the base: : TaskRunner is base: : base class base SequencedTaskRunnerand: : SingleThreadTaskRunner, So scoped_refptr TaskRunner members can accommodate a base: : TaskRunner, abase: : SequencedTaskRunner or a base: : SingleThreadTaskRunner.

class A {
  A() = default;

  void DoSomething(a) {
    task_runner_-PostTask(FROM_HERE, base::BindOnce(A));

  scoped_refptrbase::TaskRunner task_runner_ =
Copy the code

Unless the test requires precise control over how the task is executed, it is best to call base::PostTask*() directly (see Testing to control less invasive methods in testing).

Publish order task

A sequence is a set of tasks that run once (not necessarily on the same thread) in the order of publication. Task to release as part of the sequence, please use the base: : SequencedTaskRunner.

Posting to a New Sequence

A base: : SequencedTaskRunner can create the base through the following ways: : CreateSequencedTaskRunner ().

scoped_refptrSequencedTaskRunner sequenced_task_runner =
    base::CreateSequencedTaskRunner(...). ;// TaskB runs after TaskA completes.
sequenced_task_runner-PostTask(FROM_HERE, base::BindOnce(TaskA));
sequenced_task_runner-PostTask(FROM_HERE, base::BindOnce(TaskB));
Copy the code

Publish to the current (virtual) thread

The preferred way to publish to the current thread is through Base ::CurrentThreadtrait.

// The task will run on the current (virtual) thread's default task queue.
base::PostTask(FROM_HERE, {base::CurrentThread()}, base::BindOnce(Task));

Copy the code

You can optionally specify additional characteristics. This is important because some threads (such as the browser UI thread, browser IO thread, and Blink main thread) cluster multiple task queues into the same thread, and the default priority may not be appropriate for your task.

For example, you can explicitly set the priority:

// The task will run on the current (virtual) thread's best effort queue.
// NOTE only the Browser UI and Browser IO threads support task priority (for
// now), other (virtual) threads will silently ignore traits used in combination
// with `base::CurrentThread`.
               {base::CurrentThread(), base::TaskPriority::BEST_EFFORT},
Copy the code

The base: : SequencedTaskRunner can through the following way for the current task was stationed to base: : GetContinuationTaskRunner ().

On some threads, only one task runs the program, so the current sequence is the same as the current thread. This is not the case in the browser UI, browser IO, or Blink main thread. In addition, the parallel base: : GetContinuationTaskRunner () thread pool tasks or there is no task to run, the concept of the current sequence does not exist, in this case will be DCHECK processing.

Note: * * * * although base: : GetContinuationTaskRunner () from the parallel tasks call is invalid, but the order task or single thread tasks effectively. From the base: : SequencedTaskRunner or base: : SingleThreadTaskRunner.

// The task will run after any task that has already been posted
// to the SequencedTaskRunner to which the current task was posted
// (in particular, it will run after the current task completes).
// It is also guaranteed that it won’t run concurrently with any
// task posted to that SequencedTaskRunner.
base::GetContinuationTaskRunner() - PostTask(FROM_HERE, base::BindOnce(Task));
Copy the code

You can also get the default task runner for the CurrentThread with the base::CurrentThread feature, but you can specify other characteristics. This is important because some threads, such as the browser UI thread and Blink main thread, cluster multiple task queues into the same thread, and the default priority may not be appropriate for your task. For example, you can explicitly set the priority:

// The task will run on the current (virtual) thread's best effort queue.
// NOTE only the Browser UI and Browser IO threads support task priority, other
// (virtual) threads will silently ignore traits used in combination with
// `base::CurrentThread`.
               {base::CurrentThread(), base::TaskPriority::BEST_EFFORT},
Copy the code

If you need to get these characteristics of the mission, will be the base to: : CreateSequencedTaskRunner ().

// Tasks posted to |task_runner| will run on the current (virtual) thread's best
// effort queue.
auto task_runner = base::CreateSequencedTaskRunner(
     {base::CurrentThread(), base::TaskPriority::BEST_EFFORT});
Copy the code

Use sequences instead of locks

Locking is discouraged in Chrome. Sequences inherently provide thread-safety. Prioritize classes that are always accessed from the same sequence, rather than using locks to manage their own thread safety.

* thread safe, but not thread affine; Why is that? ** Tasks published in the same order will run sequentially. After the sorting task is complete, the next task may be executed by another worker thread, but you can be sure that the task sees any side effects caused by the previous task on its sequence.

class A {
  A() {
    // Do not require accesses to be on the creation sequence.

  void AddValue(int v) {
    // Check that all accesses are on the same sequence.


  // No lock required, because all accesses are on the
  // same sequence.
  std::vectorint values_; }; A a; scoped_refptrSequencedTaskRunner task_runner_for_a = ... ; task_runner_for_a-PostTask(FROM_HERE,
                      base::BindOnce(A::AddValue, base::Unretained(a), 42));
                      base::BindOnce(A::AddValue, base::Unretained(a), 27));

// Access from a different sequence causes a DCHECK failure.scoped_refptrSequencedTaskRunner other_task_runner = ... ; other_task_runner-PostTask(FROM_HERE,
                            base::BindOnce(A::AddValue, base::Unretained(a), 1));
Copy the code

Locks are only used to exchange shared data structures that can be accessed on multiple threads. If a thread is updating it based on expensive calculations or through disk access, slow work should be done without holding a lock. Locks should be used to exchange new data only if the results are available. An example of this is in PluginList :: LoadPlugins (content/browser/ if locks must be used, here are some best practices and pitfalls to avoid.

Many of the apis in Chrome are asynchronous in order to write non-blocking code. In general, this means that they either need to be executed on a specific thread/sequence and will return results through a custom delegate interface, or they take the object called base::Callback when the requested operation completes. Executing work on specific threads/sequences is described in the PostTask section above.

Publish multiple tasks to the same thread

If multiple tasks need to run on the same thread, please release it to the base: : SingleThreadTaskRunner. All issued to the same task in base: : SingleThreadTaskRunner in release order run on the same thread.

Publish to the main thread or IO thread in the browser process

base::PostTask(FROM_HERE, {content::BrowserThread::UI}, ...) ; base::CreateSingleThreadTaskRunner({content::BrowserThread::IO})
    -PostTask(FROM_HERE, ...) ;Copy the code

The main thread and the IO thread are already very busy. Therefore, it is best to publish to generic threads whenever possible (see Publishing parallel Tasks, publishing serialized tasks). A good reason to publish to the main thread is to update the UI or access objects bound to it (such as profiles). A good reason to publish to AN IO thread is to access the internals of its bound components (e.g., IPC, network). Note: There is no explicit publishing task for the IO thread to send/receive IPC or send/receive data over the network.

Published to the main thread during the renderer

Publish to custom SingleThreadTaskRunner

If multiple tasks need to be run on the same thread, the thread doesn't have to be the main thread or IO thread, it is published to the base: : SingleThreadTaskRunner founder base: : CreateSingleThreadTaskRunner.

scoped_refptrSingleThreadTaskRunner single_thread_task_runner =
    base::CreateSingleThreadTaskRunner(...). ;// TaskB runs after TaskA completes. Both tasks run on the same thread.
single_thread_task_runner-PostTask(FROM_HERE, base::BindOnce(TaskA));
single_thread_task_runner-PostTask(FROM_HERE, base::BindOnce(TaskB));
Copy the code

Remember to use sequences rather than physical threads whenever possible, so this is hardly necessary.

Publish tasks to COM single Threaded unit (STA) threads (Windows)

Task is the need to be in a COM single-threaded apartment (STA) operation must be posted to a base on the thread: : SingleThreadTaskRunner by returning base: : CreateCOMSTATaskRunner (). As will be released multiple tasks to base: : SingleThreadTaskRunner as described in the same thread released all of the tasks to the same thread in release order run on the same thread.

// Task(A|B|C)UsingCOMSTA will run on the same COM STA thread.

void TaskAUsingCOMSTA(a) {
  // [ This runs on a COM STA thread. ]

  // Make COM STA calls.
  // ...

  // Post another task to the current COM STA thread.
  base::ThreadTaskRunnerHandle::Get() - PostTask(
      FROM_HERE, base::BindOnce(TaskCUsingCOMSTA));
void TaskBUsingCOMSTA(a) {}void TaskCUsingCOMSTA(a) {}auto com_sta_task_runner = base::CreateCOMSTATaskRunner(...). ; com_sta_task_runner-PostTask(FROM_HERE, base::BindOnce(TaskAUsingCOMSTA));
com_sta_task_runner-PostTask(FROM_HERE, base::BindOnce(TaskBUsingCOMSTA));
Copy the code

Annotate tasks with TaskTraits

Base ::TaskTraits encapsulate information about tasks that help thread pools make better scheduling decisions.

All the base::PostTask*() functions in base/task/post_task.h have an overload that takes the Base ::TaskTraits argument, rather than a single argument. No Overloading of base::TaskTraits as arguments applies to the following tasks:

  • Don't block (see MayBlock and Primitives with Base SCRIPT).
  • Precedence inherits the current precedence rather than specifying its own precedence.
  • You can either prevent closure or skip it when it is closed (the thread pool is free to choose the appropriate default value). Tasks that do not match this description must be published using explicit TaskTraits.

Base /task/task_traits. H provides detailed documentation of available traits. Content layer also provides other features, the content/public/browser/browser_task_traits h task can be released to the BrowserThread.

Here are some examples of how to specify base::TaskTraits.

// This task has no explicit TaskTraits. It cannot block. Its priority
// is inherited from the calling context (e.g. if it is posted from
// a BEST_EFFORT task, it will have a BEST_EFFORT priority). It will either
// block shutdown or be skipped on shutdown.
base::PostTask(FROM_HERE, base::BindOnce(...). );// This task has the highest priority. The thread pool will try to
// run it before USER_VISIBLE and BEST_EFFORT tasks.
    FROM_HERE, {base::TaskPriority::USER_BLOCKING},
    base::BindOnce(...). );// This task has the lowest priority and is allowed to block (e.g. it
// can read a file from disk).
    FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
    base::BindOnce(...). );// This task blocks shutdown. The process won't exit before its
// execution is complete.
    FROM_HERE, {base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
    base::BindOnce(...). );// This task will run on the Browser UI thread.
    FROM_HERE, {content::BrowserThread::UI},
    base::BindOnce(...). );// This task will run on the current virtual thread (sequence).
    FROM_HERE, {base::CurrentThread()},
    base::BindOnce(...). );// This task will run on the current virtual thread (sequence) with best effort
// priority.
    FROM_HERE, {base::CurrentThread(), base::TaskPriority::BEST_EFFORT},
    base::BindOnce(...). );Copy the code

Keep browser responsive

Do not perform expensive work on the main thread, IO thread, or any sequence that is expected to run tasks with low latency. But using base: : PostTaskAndReply * () or asynchronous execution of expensive work base: : SequencedTaskRunner: : PostTaskAndReply (). Note that asynchronous/overlapping I/O on IO threads is fine.

Example: Running the following code on the main thread will prevent the browser from responding to user input for too long.

// GetHistoryItemsFromDisk() may block for a long time.
// AddHistoryItemsToOmniboxDropDown() updates the UI and therefore must
// be called on the main thread.
Copy the code

The following code through GetHistoryItemsFromDisk () to call is arranged in the thread pool, then AddHistoryItemsToOmniboxDropdown () to the original sequence (in this case is given priority to the thread) calls for scheduling, so as to solve the problem. The return value from the first call is automatically supplied as an argument to the second call.

    FROM_HERE, {base::MayBlock()},
    base::BindOnce(GetHistoryItemsFromDisk, "keyword"),
Copy the code

Delaying publishing tasks

Delay publishing a one-off task

To release must run after delay due task at a time, please use the base: : PostDelayedTask * () or base: : TaskRunner: : PostDelayedTask ().

  FROM_HERE, {base::TaskPriority::BEST_EFFORT}, base::BindOnce(Task),

scoped_refptrbase::SequencedTaskRunner task_runner =
    FROM_HERE, base::BindOnce(Task), base::TimeDelta::FromHours(1));
Copy the code

** Note: ** a task delayed 1 hour may not have to run immediately when its delay expires. Specify base::TaskPriority::BEST_EFFORT to prevent its delay from slowing down the browser when it expires.

Delay publishing repetitive tasks

To publish tasks that must be run periodically, use Base ::RepeatingTimer.

class A {
  ~A() {
    // The timer is stopped automatically when it is deleted.
  void StartDoingStuff(a) {
    timer_.Start(FROM_HERE, TimeDelta::FromSeconds(1),
                 this, MyClass::DoStuff);
  void StopDoingStuff(a) {
    timer_.Stop(a); }private:
  void DoStuff(a) {
    // This method is called every second on the sequence that invoked
    // StartDoingStuff().
  base::RepeatingTimer timer_;
Copy the code

Cancel the task

Using base: : WeakPtr

Base ::WeakPtr can be used to ensure that any callbacks that destroy bindings to this object are cancelled.

int Compute(a) {... }class A {
  void ComputeAndStore(a) {
    // Schedule a call to Compute() in a thread pool followed by
    // a call to A::Store() on the current sequence. The call to
    // A::Store() is canceled when |weak_ptr_factory_| is destroyed.
    // (guarantees that |this| will not be used-after-free).
        FROM_HERE, base::BindOnce(Compute),
        base::BindOnce(A::Store, weak_ptr_factory_.GetWeakPtr()));

  void Store(int value) { value_ = value; }

  int value_;
  base::WeakPtrFactoryA weak_ptr_factory_{this};
Copy the code

Note: WeakPtr is not thread-safe: GetWeakPtr(), ~WeakPtrFactory() and Compute() (bound to WeakPtr) must all run in the same order.

Use base :: CancelableTaskTracker

Base: : CancelableTaskTracker allowed to cancel the order with a mission of different order. Remember that CancelableTaskTracker cannot cancel a task that is already running.

auto task_runner = base::CreateTaskRunner({base::ThreadPool()});
base::CancelableTaskTracker cancelable_task_tracker;
cancelable_task_tracker.PostTask(task_runner.get(), FROM_HERE,
// Cancels Task(), only if it hasn't already started running.
cancelable_task_tracker.TryCancelAll(a);Copy the code


For more details, see Components for Testing a Publish Task.

To test the code USES the base: : ThreadTaskRunnerHandle, base: : SequencedTaskRunnerHandle or base to a function/task/post_task. H, Instantiate a base: : test: : TaskEnvironment for testing. If you need BrowserThreads, please use the content: : BrowserTaskEnvironment instead of base: : test: : TaskEnvironment.

Test can base: : test: : TaskEnvironment used to run a message pump base: : RunLoop, can allow it to run to the Quit () (explicitly or through the RunLoop: : QuitClosure ()), Or RunUntilIdle() runs to the ready task and returns immediately.

TaskEnvironment sets RunLoop :: Run () to LOG (FATAL) if it has not explicitly exited after TestTimeouts :: action_timeout (). If the code under test fails to trigger a RunLoop exit, it is best to suspend the test. You can override the timeout using ScopedRunTimeoutForTest.

class MyTest : public testing::Test { public: // ... protected: base::test::TaskEnvironment task_environment_; }; TEST(MyTest, MyTest) { base::ThreadTaskRunnerHandle::Get()-PostTask(FROM_HERE, base::BindOnce(A)); base::SequencedTaskRunnerHandle::Get()-PostTask(FROM_HERE, base::BindOnce(B)); base::ThreadTaskRunnerHandle::Get()-PostDelayedTask( FROM_HERE, base::BindOnce(C), base::TimeDelta::Max()); // This runs the (Thread|Sequenced)TaskRunnerHandle queue until it is empty. // Delayed tasks are not added to the queue  until they are ripe for execution. base::RunLoop().RunUntilIdle(); // A and B have been executed. C is not ripe for execution yet. base::RunLoop run_loop; base::ThreadTaskRunnerHandle::Get()-PostTask(FROM_HERE, base::BindOnce(D)); base::ThreadTaskRunnerHandle::Get()-PostTask(FROM_HERE, run_loop.QuitClosure()); base::ThreadTaskRunnerHandle::Get()-PostTask(FROM_HERE, base::BindOnce(E)); // This runs the (Thread|Sequenced)TaskRunnerHandle queue until QuitClosure is // invoked. run_loop.Run(); // D and run_loop.QuitClosure() have been executed. E is still in the queue. // Tasks posted to thread pool run asynchronously as they are posted. base::PostTask(FROM_HERE, {base::ThreadPool()}, base::BindOnce(F)); auto task_runner = base::CreateSequencedTaskRunner({base::ThreadPool()}); task_runner-PostTask(FROM_HERE, base::BindOnce(G)); // To block until all tasks posted to thread pool are done running: base::ThreadPoolInstance::Get()-FlushForTesting(); // F and G have been executed. base::PostTaskAndReplyWithResult( FROM_HERE, base::TaskTrait(), base::BindOnce(H), base::BindOnce(I)); // This runs the (Thread|Sequenced)TaskRunnerHandle queue until both the // (Thread|Sequenced)TaskRunnerHandle queue and  the TaskSchedule queue are // empty: task_environment_.RunUntilIdle(); // E, H, I have been executed. }Copy the code

Use ThreadPool in the new process

ThreadPoolInstance needs to be initialized in the process before it can be used by base/task/post_task.h. Initialization of ThreadPoolInstance in Chrome processes and child processes (renderers, Gpus, utilities) has been handled. To use ThreadPoolInstance in another process, initialize ThreadPoolInstance early in the main function:

// This initializes and starts ThreadPoolInstance with default params.
base::ThreadPoolInstance::CreateAndStartWithDefaultParams(" process_name ");// The base/task/post_task.h API can now be used with base::ThreadPool trait.
// Tasks will be // scheduled as they are posted.

// This initializes ThreadPoolInstance.
base::ThreadPoolInstance::Create(" process_name ");// The base/task/post_task.h API can now be used with base::ThreadPool trait. No
// threads will be created and no tasks will be scheduled until after Start() is
// called.
base::ThreadPoolInstance::Get() - Start(params);
// ThreadPool can now create threads and schedule tasks.
Copy the code

And turn off ThreadPoolInstance's main functionality at a later stage:

base::ThreadPoolInstance::Get() - Shutdown(a);// Tasks posted with TaskShutdownBehavior::BLOCK_SHUTDOWN and
// tasks posted with TaskShutdownBehavior::SKIP_ON_SHUTDOWN that
// have started to run before the Shutdown() call have now completed their
// execution. Tasks posted with
// TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN may still be
// running.
Copy the code

TaskRunner ownership (encourages no dependency injection)

TaskRunners should not be delivered through multiple components. Instead, the component that uses TaskRunner should be the component that created it.

See this refactoring example where TaskRunner is passed through many components and used only in the final leaf. The leaf can and should now get its TaskRunner base/task/post_task.h directly from there.

As mentioned above, base: : test: : TaskEnvironment allow unit test control from basic TaskRunner release task. In a few cases, testing requires more precise control over the order of tasks: TaskRunners' dependency injection can be useful. In this case, the preferred method is as follows:

class Foo {

  // Overrides |background_task_runner_| in tests.
  void SetBackgroundTaskRunnerForTesting( scoped_refptr
    background_task_runner_ = std::move(background_task_runner);

  scoped_refptrbase::SequencedTaskRunner background_task_runner_ =
          {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
Copy the code

Note that this still allows you to remove all pipe layers between // Chrome and the component, since unit tests will use leaf layers directly.

The old API

The code base contains several older apis for task runners that retrieve the current thread and current sequence. These are being migrated to new apis and should not be used in new code.

Base: : ThreadTaskRunnerHandle return the current thread's default task to run the program. All calling sites will be migrated to use Base :: CurrentThread.

// The task will run on the current thread in the future.
base::ThreadTaskRunnerHandle::Get() - PostTask(
    FROM_HERE, base::BindOnce(Task));
Copy the code

Base: : SequencedTaskRunnerHandle: : Get () returns the thread of the default task runner (browser UI thread, the browser IO thread, Blink email thread), or in the thread pool tasks of sorted returns the current sequence. All call site will migrate to use base: : CurrentThread or base: : GetContinuationTaskRunner () depends on the call site

// The task will run after any task that has already been posted
// to the SequencedTaskRunner to which the current task was posted
// (in particular, it will run after the current task completes).
// It is also guaranteed that it won’t run concurrently with any
// task posted to that SequencedTaskRunner.
base::SequencedTaskRunnerHandle::Get() - PostTask(FROM_HERE, base::BindOnce(Task));
Copy the code

Callbacks and Bind ()

The templated Base ::Callback class is a generic function object. Together with the functions in base::Bind()base/bind.h, they provide a type-safe method for executing partial function applications.

Partial applications (or "currying") are the process of binding a subset of a function's arguments to produce another function that requires fewer arguments. This can be used to pass units of delayed execution, just as lexical closures are used in other languages. For example, it is used in Chromium code to schedule tasks on different MessageLoops.

Callbacks without unbound input arguments (base::Callback ) are called base::Closure. Note that this differs from the way other languages call closures - it does not retain references to its enclosing environment. ()

OnceCallback and RepeatingCallback

Base ::OnceCallback and Base ::RepeatingCallback is the next generation callback class, which is under development.

Base ::OnceCallback Created by base::BindOnce(). This is a moveable type of callback variant that can only be run once. By default, this moves the binding parameter from its internal storage to the binding function, so movable types are easier to use. This should be the preferred type of callback: since the life of the callback is clear, it is easier to infer when a callback passed between threads has been broken.

RepeatingCallback Create base::BindRepeating(). This is a replicable callback variant that can be run multiple times. It uses internal reference counting to make copies cheap. However, because ownership is shared, it is difficult to infer when the callback and binding state are broken, especially when callbacks are passed between threads.

This old base::Callback is currently alias base::RepeatingCallback. In the new code, use base::OnceCallback where possible and base::RepeatingCallback otherwise. When the migration is complete, the type alias, Base ::OnceCallback, is removed and renamed Base ::Callback to emphasize that it is preferred.

RepeatingCallback Can be converted to base::OnceCallback by implicit conversion.

Memory management and delivery

Base ::{Once,Repeating}Callback Passes objects by value if ownership is transferred; Otherwise, it is passed through const-reference.

// |Foo| just refers to |cb| but doesn't store it nor consume it.
bool Foo(const base::OnceCallbackvoid(int) cb) {
  return cb.is_null(a); }// |Bar| takes the ownership of |cb| and stores |cb| into |g_cb|.
base::RepeatingCallbackvoid(int) g_cb;
void Bar(base::RepeatingCallbackvoid(int) cb) {
  g_cb = std::move(cb);

// |Baz| takes the ownership of |cb| and consumes |cb| by Run().
void Baz(base::OnceCallbackvoid(int) cb) {

// |Qux| takes the ownership of |cb| and transfers ownership to PostTask(),
// which also takes the ownership of |cb|.
void Qux(base::RepeatingCallbackvoid(int) cb) {
  PostTask(FROM_HERE, base::BindOnce(cb, 42));
  PostTask(FROM_HERE, base::BindOnce(std::move(cb), 43));
Copy the code

When passing the base::{Once,Repeating}Callback object to function arguments, use STD ::move() if you do not need to keep a reference to it, otherwise, pass the object directly. You may see a compilation error when the function requires exclusive ownership and no callbacks are passed by movement. Notice that move-frombase::{Once,Repeating}Callback becomes null, as if its Reset() method had been called. After that, its is_NULL () method will return true and its operator bool() will return false.

Basic content quick reference

Binding bare functions

int Return5(a) { return 5; }
base::OnceCallbackint() func_cb = base::BindOnce(Return5);
LOG(INFO)  std::move(func_cb).Run(a);// Prints 5.
Copy the code
int Return5(a) { return 5; }
base::RepeatingCallbackint() func_cb = base::BindRepeating(Return5);
LOG(INFO)  func_cb.Run(a);// Prints 5.
Copy the code

Bind no captured Lambda

base::Callbackint() lambda_cb = base::Bind([] { return 4; });
LOG(INFO)  lambda_cb.Run(a);// Print 4.

base::OnceCallbackint() lambda_cb2 = base::BindOnce([] { return 3; });
LOG(INFO)  std::move(lambda_cb2).Run(a);// Print 3.
Copy the code

Bind captured Lambda (under test)

#include "base/test/bind_test_util.h"

int i = 2;
base::Callbackvoid() lambda_cb = base::BindLambdaForTesting([]() { i++; });
lambda_cb.Run(a);LOG(INFO)  i;  // Print 3;
Copy the code

Bound class method

The first argument to bind is the member function to be called, and the second is the object to be called on.

class Ref : public base::RefCountedThreadSafeRef {
  int Foo(a) { return 3; }}; scoped_refptrRef ref =new Ref(a); base::Callbackvoid() ref_cb = base::Bind(Ref::Foo, ref);
LOG(INFO)  ref_cb.Run(a);// Prints out 3.
Copy the code

By default, the object must support RefCounted, or a compiler error will occur. If you want to pass between threads, make sure it is RefCountedThreadSafe! If you don't want to use reference counting, see "Advanced binding of member functions" below.

Run the callback

Callbacks can be Run using their Run method, which has the same signature as the callback's template parameters. Note that it consumes the callback object base::OnceCallback::Run and can only be called on the callback rvalue.

void DoSomething(const base::Callbackvoid(int, std::string) callback) {

void DoSomethingOther(base::OnceCallbackvoid(int, std::string) callback) {
Copy the code

RepeatingCallbacks can be run multiple times (without being deleted or marked at run time). However, this excludes the use of base::Passed (see below).

void DoSomething(const base::RepeatingCallbackdouble(double) callback) {
  double myresult = callback.Run(3.14159);
  myresult += callback.Run(2.71828);
Copy the code

If running a callback could cause it to destroy itself (for example, if the callback recipient deletes the object to which the callback belongs), the callback should be moved before it is safe to invoke it. (Note that this is only a RepeatingCallbacks issue because the OneCallback must be moved all the way once to execute.)

void Foo::RunCallback(a) {
  std::move(foo_deleter_callback_).Run(a); }Copy the code

Create a callback that has no effect

Sometimes, you need a callback that does nothing at run time (for example, test code that does not want to be notified of certain event types). You might be tempted to pass a callback to the correct type of default construct:

using MyCallback = base::OnceCallbackvoid(bool arg);
void MyFunction(MyCallback callback) {
  std::move(callback).Run(true);  // Uh oh...}...MyFunction(MyCallback());  / /... this will crash when Run()!
Copy the code

The callback of the default construct is NULL, so it cannot be Run (). Instead use base::DoNothing() :

.MyFunction(base::DoNothing());  // Can be Run(), will no-op
Copy the code

Base ::DoNothing() can be passed to any OnceCallback or RepeatingCallback that returns void.

On the implementation side, base::DoNothing() is actually a functor from which it generates a callback operator(). This makes it unavailable when trying to bind other parameters to it. Generally, the parameter is bound to the DoNothing () the only reason is to manage the lifetime of an object, in this case, you should try to use DeleteSoon (), ReleaseSoon () or RefCountedDeleteOnSequence usage. If you really need to bind the argument to DoNothing (), or if you need to explicitly create a callback object (because implicit conversions via operator () () will not compile), you can instantiate it directly:

// Binds |foo_ptr| to a no-op OnceCallback takes a scoped_refptrFoo.
// ANTIPATTERN WARNING: This should likely be changed to ReleaseSoon()!
base::Bind(base::DoNothing::Oncescoped_refptrFoo(), foo_ptr);
Copy the code

Pass unbound input parameters

The unbound argument is Run() specified during the callback. They are specified in the Base ::Callback template type:

void MyFunc(int i, const std::string str) {}
base::Callbackvoid(int.const std::string) cb = base::Bind(MyFunc);
cb.Run(23."hello, world");
Copy the code

Pass binding input parameters

When you create the callback, specify the base::Bind() parameter as the binding parameter. They are passed to the function, and the ner of the Run() callback doesn't see those values or even know what function it is calling.

void MyFunc(int i, const std::string str) {}
base::Callbackvoid() cb = base::Bind(MyFunc, 23."hello world");
cb.Run(a);Copy the code

Callbacks without unbound input arguments (base::Callback ) are called base::Closure. So we could also write: ()

base::Closure cb = base::Bind(MyFunc, 23."hello world");
Copy the code

When a member function is called, the binding parameter comes only after the object pointer.

base::Closure cb = base::Bind(MyClass::MyFunc, this.23."hello world");
Copy the code

Partial binding of parameters (solidification)

You can specify some parameters when creating the callback and the rest when executing the callback.

When a function is called, bound arguments are the first, followed by unbound arguments.

void ReadIntFromFile(const std::string filename,
                     base::OnceCallbackvoid(int) on_read);

void DisplayIntWithPrefix(const std::string prefix, int result) {
  LOG(INFO)  prefix  result;

void AnotherFunc(const std::string file) {
  ReadIntFromFile(file, base::BindOnce(DisplayIntWithPrefix, "MyPrefix: "));
Copy the code

This technique is called Curinging. It should be used instead of creating an adapter class that contains binding parameters. Also note that the "MyPrefix: "argument is actually a const char*, while DisplayIntWithPrefix actually requires a const STD ::string. Base ::Bind is enforced as normal function dispatch, if possible.

Avoid replication with callback parameters

The base::BindRepeating() or argument base::BindOnce() is moved to its internal storage when passed as an rvalue.

std::vectorint v = {1.2.3};
// |v| is moved into the internal storage without copy.
base::Bind(Foo, std::move(v));
Copy the code
// The vector is moved into the internal storage without copy.
base::Bind(Foo, std::vectorint ({1.2.3}));
Copy the code

Base ::BindOnce() always moves the bound argument to the target function if possible. Function parameters that are passed by value and have a move constructor are moved rather than copied. This makes it easy to combine only movement types with using base::BindOnce().

In contrast, the base::BindRepeating() argument bound to the argument is moved to the target function base::Passed() only if it is bound to the argument.

Danger: Base ::RepeatingCallback If arguments are bound, A can only run base::Passed() once. Therefore, avoid base::Passed(). If you know that the callback will only be called once, it's a good idea to refactor the code to use Base ::OnceCallback.

Avoid base::Passed() using base::BindOnce() with and, STD ::move() and more familiar.

void Foo(std::unique_ptrint) {}
auto p = std::make_uniqueint (42);

// |p| is moved into the internal storage of Bind(), and moved out to |Foo|.
base::BindOnce(Foo, std::move(p));
base::BindRepeating(Foo, base::Passed(p)); // Ok, but subtle.
base::BindRepeating(Foo, base::Passed(std::move(p))); // Ok, but subtle.
Copy the code

Quick reference for advanced binding

Bind class methods with weak Pointers

If MyClass has a Base ::WeakPtr Weak_this_ member (see below), class methods can be bound using the following method:

base::Bind(MyClass::Foo, weak_this_);
Copy the code

If the object has been destroyed, the callback will not run.

Note that class method callbacks bound to Base ::WeakPtrs can only run on the same sequence of destroying the object, because otherwise the execution of the callback may compete with the deletion of the object.

Base ::WeakPtr works with base::Bind(), MyClass usually looks like this:

class MyClass {
  MyClass() {
    weak_this_ = weak_factory_.GetWeakPtr(a); }private:
  base::WeakPtrMyClass weak_this_;
  // MyClass member variables go here.
  base::WeakPtrFactoryMyClass weak_factory_{this};
Copy the code

Weak_factory_ is the last member variable, so MyClass destroys it first. This ensures that if any of the bound class methods Weak_this_ both Run() during disassembly, they will not actually be executed.

If MyClass only base::Bind() executes s in the same order and performs callbacks, it is usually safe to call Weak_factory_.getWeakptr () that base::Bind() calls instead of weak_this_ being called separately during construction.

Bind class methods to manual lifecycle management

base::Bind(MyClass::Foo, base::Unretained(this));
Copy the code

This will disable all lifetime management on the object. It is your responsibility to ensure that the object is still active when invoked. You break it, you own it!

Bind class methods and let the class have callbacks

MyClass* myclass = new MyClass;
base::Bind(MyClass::Foo, base::Owned(myclass));
Copy the code

After the callback is destroyed, the object will be deleted even if it is not running (just as you would publish a task during shutdown). May be useful in "dismissal" cases.

STD ::unique_ptr also supports the use of smart Pointers (for example) as receivers.

std::unique_ptrMyClass myclass(new MyClass);
base::Bind(MyClass::Foo, std::move(myclass));
Copy the code

Ignore the return value

Sometimes you want to call a function that returns a value in a callback that is not expected to return a value.

int DoSomething(int arg) { cout  arg  endl; }
base::Callbackvoid(int) cb =
Copy the code

A quick reference to Bind parameters to Bind ()

The binding argument is specified as the function's argument base::Bind() and passed to the function. Callbacks with no arguments or with no unbound arguments are called base::Closure (base::Callback and base::Closure are the same thing). ()

Pass arguments owned by the callback

void Foo(int* arg) { cout  *arg  endl; }
int* pn = new int(1);
base::Closure foo_callback = base::Bind(foo, base::Owned(pn));
Copy the code

This parameter is removed after destruction even if no callback is run (for example, if you publish a task during a shutdown).

Pass the parameter as unique_ptr

void TakesOwnership(std::unique_ptrFoo arg) {}
auto f = std::make_uniqueFoo();
// f becomes null during the following call.
base::OnceClosure cb = base::BindOnce(TakesOwnership, std::move(f));
Copy the code

Ownership of this parameter will remain in the callback until the callback is run, and then ownership is passed to the callback function. This means that the callback can only run once. If the callback never runs, the object is deleted when it is destroyed.

Pass the parameter as scoped_refptr

void TakesOneRef(scoped_refptrFoo arg) {}
scoped_refptrFoo f(new Foo);
base::Closure cb = base::Bind(TakesOneRef, f);
Copy the code

This should "work". As long as it is active, the closure takes one reference and another reference to the called function.

void DontTakeRef(Foo* arg) {}
scoped_refptrFoo f(new Foo);
base::Closure cb = base::Bind(DontTakeRef, base::RetainedRef(f));
Copy the code

Base ::RetainedRef preserves the reference to the object and passes the original pointer to the object when the callback is run.

Pass parameters by reference

References are copied unless used with or. Example: the STD: : ref ` ` STD: : which

void foo(const int arg) { printf("%d %p\n", arg, arg); }
int n = 1;
base::Closure has_copy = base::Bind(foo, n);
base::Closure has_ref = base::Bind(foo, std::cref(n));
n = 2;
foo(n);                        // Prints "2 0xaaaaaaaaaaaa"
has_copy.Run(a);// Prints "1 0xbbbbbbbbbbbb"
has_ref.Run(a);// Prints "2 0xaaaaaaaaaaaa"
Copy the code

Typically, arguments are copied into closures. Danger: STD ::ref and STD ::cref store (constant) references to original arguments. This means that you must ensure that the object outlives the callback!

The implementation of instructions

Where the design comes from:

The design of base::Callback and Base ::Bind is heavily C ++ influenced by tr1::function/ tr1:: Bind and the 'Google Callback' system used internally by Google.

Custom behavior

There are several injection points that can control binding behavior externally from their implementation.

namespace base {

template typename Receiver
struct IsWeakReceiver {
  static constexpr bool value = false;

template typename Obj
struct UnwrapTraits {
  template typename T
  T Unwrap(T obj) {
    returnstd::forwardT(obj); }}; }// namespace base
Copy the code

If Base ::IsWeakReceiver ::value is true on the Receiver of the base::Bind method, then check if the Receiver's evaluation is true and cancel the call if its evaluation is false. You can specialise in base::IsWeakReceiver to treat external smart Pointers as weak Pointers.

Base ::UnwrapTraits ::Unwrap() is called for each bound parameter before base::Callback calls the target function. You can focus on the wrapping of this definition argument, such as Base ::Unretained, Base ::Owned, Base ::RetainedRef, and Base ::Passed.

Implementation principle:

The system consists of three main components:

  1. thebase::CallbackClass.
  2. thebase::Bind()Function.
  3. Parameter wrappers (e.gbase::Unretained()andbase::Owned()).

The callback class represents a generic function pointer. Internally, it stores the state representing the state translation of the target function and all its bound parameters. The base::Callback construct requires base::BindStateBase*, which is derived from upcasted Base ::BindState. In the context of a constructor, the static type of this base::BindState pointer uniquely identifies the function it represents, all its binding parameters, and the methods that Run() can call the target.

Base ::Bind() creates base::BindState that is fully statically typed and erases the target function type and the type of the binding parameter. It does this by storing a pointer to a particular Run() function and uploading its state to Base ::BindState* base::BindStateBase*. This is safe as long as the BindStateBase pointer is used only with the stored Run() pointer.

To base::BindState internally created object base::Bind() functionality. These functions are responsible for as well as a set of internal templates

  • Decompress the function signature into return types and parameters
  • Determines the number of parameters to bind
  • Create BindState for the storage binding parameter
  • Performing compile-time assertions avoids error-prone behavior
  • Returns aCallbackA with Arity matching the number of unbound parameters, and knowing that the correct reference semantics of the target object are known if we bind a method.

These base::Bind functions do this using type inference and mutable parameter templates.

By default, base::Bind() stores copies of all Bind arguments, and attempts to reference the target object if the bound function is a class method. These copies are created even if the function references the parameters as const. (To disallow binding to nonconst references, see bind.h.)

To change this behavior, we introduced a set of parameter wrappers (such as base::Unretained()). These are simple container templates that are passed by value and wrap Pointers to parameters. For more information, see file-level comments in base/bind_helpers.h.

These types will be passed to the Unwrap() function to modify the behavior base::Bind(). These Unwrap() functions change the behavior by partially professionalizing the parameters based on whether they are wrapper types.

Base ::Unretained() is chrome specific.

The lack of function

  • Binds an array to a function that takes a nonconstant pointer. Ex. :
void Foo(const char* ptr);
void Bar(char* ptr);
base::Bind(Foo, "test");
base::Bind(Bar, "test");  // This fails because ptr is not const.
Copy the code
  • If the parameter is partially bound, there may be an unbound parameter before the bound parameter. Ex. :
void Foo(int x, bool y);
base::Bind(Foo, _1, false); // _1 is a placeholder.
Copy the code

If you want base::Callback to declare forward in its own header file, use "base/callback_forward.h" instead.

Chromium UI

Overview and Background

Windows provides very primitive tools for building user interfaces. The system provides some basic controls and native window containers, but building custom user interfaces is difficult. Because Chromium was expected to have a different aesthetic, the framework had to be built on Windows to speed up the development of custom UIs. This system is called the View.

Views ** is a rendering system, unlike the systems used to render web pages in WebKit or Gecko. The user interface consists of a tree of components called Views. These views are responsible for rendering, layout, and event handling. Each view in the tree represents a different component of the UI. The analogue is a hierarchy of HTML documents.

At the root of the View hierarchy is the Widget, which is the native window. The native window receives messages from Windows, converts them into something that the View hierarchy can understand, and then passes them to the RootView. The RootView then starts propagating events into the View hierarchy.

Painting and layout are done in a similar way. A View in a View tree has its own boundaries (usually inserted through the Layout method of the View it contains), so when asked to Paint it, it draws a canvas clipped to its boundaries and converts the origin of the rendering to the upper left corner of the View. When the Paint message is received, the entire View tree is rendered into a single canvas set and owned by the Widget. The rendering itself is done using a combination of Skia and GDI calls -GDI for text and Skia for everything else.

However, many OF the UI controls in Chromium's UI don't use view rendering. Instead, they are native Windows controls hosted in a special view that knows how to display and resize native widgets. These are used for buttons, tables, radio buttons, check boxes, text fields, and other such controls. Because they use native controls, these views are not particularly portable either, except perhaps in the API.

Unless platform-specific rendering code, code for resizing content according to system metrics, etc., the rest of the View system is not particularly hard to port, as most rendering is done using the cross-platform Skia library. For historical reasons, much of the View's functionality was of Windows or ATL type, but since then we have enhanced UI/GFX/with a number of platform-independent types that can eventually be replaced with these types.

Code location and information

The basic set of classes and interfaces provided by the view can be found in the SRC/UI/Views/directory. All the base view classes are in the "Views" namespace.

Universal widget

In the view frame:

- WidgetWin** : Base class for all widgets in a view. Provides a basic implementation of child window controls. If you do not create a top-level window or dialog box, subclass it directly. - Window: top-level window. A subclass of WidgetWin.

For more information on building dialogs and other windowed UIs using Window, CustomFrameWindow, etc., read Viewing Windowing.

On the Chromium browser front end:

- **BrowserFrame: A subclass of Window that provides additional message processing for the Browser Window in Chrome. See the browser window. - ConstrainedWindowImpl: A subclass of Window that provides a framework for constrained dialogs, such as HTTP basic authentication hints.

Other methods

At the start of the project, we started building the Chromium browser using native Windows and the owner drawing method used in many Windows applications. This proved unsatisfactory, as native Windows themselves did not support transparency and event handling required cumbersome window subclassing. Some of the early UI elements tended to use custom painting and event handling (for example, autocomplete), but this was temporary based on the situation.

Windows' existing UI toolkit is equally unsatisfactory, with a limited widget set, an unnatural look, or a clunky programming model.


Overall, views make it relatively easy to build complex custom UIs. However, it has some rough edges that can be improved over time:

- Currently, event types sometimes have problems - they decipher native Windows message parameters and then discard them. Sometimes, this information is useful. - Some point-to-point message processing. - Native controls do not work properly until they are inserted into the View hierarchy attached to the Widget with a valid HWND. Many of our native controls have API methods that require them to exist in a window hierarchy. This means that they cannot be fully initialized before insertion. The View API will eventually be improved to make it clearer (error 5191). - The basic Widget interface itself is somewhat frozen in time. Some improvements and consolidation are worthwhile.


About (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.