Chromium Developer Documentation

Google Chrome is built with open source code from Chromium.

Except as otherwise noted, the content of this page is licensed under a Creative Commons Attribution 2.5 license, and examples are licensed under the BSD License.

Threading

Overview

Chromium is a very multithreaded product. We try to keep the UI as responsive as possible, and this means not blocking the UI thread with any blocking I/O or other expensive operations. Our approach is to use message passing as the way of communicating between threads. We discourage locking and threadsafe objects. Instead, objects live on only one thread, we pass messages between threads for communication, and we use callback interfaces (implemented by message passing) for most cross-thread requests.

The Thread object is defined in base/thread.h. In general you should probably use one of the existing threads described below rather than make new ones. We already have a lot of threads that are difficult to keep track of. Each thread has a MessageLoop (see base/message_loop.h) that processes messages for that thread. You can get the message loop for a thread using the Thread.message_loop() function.

Existing threads

Most threads are managed by the BrowserProcess object, which acts as the service manager for the main "browser" process. By default, everything happens on the UI thread (the main thread where the application starts up). We have pushed certain classes of processing into these other threads. It has getters for the following threads:

  • io_thread: This thread is somewhat mis-named. It is the dispatcher thread that handles communication between the browser process and all the sub-processes. It is also where all resource rqeuests (web page loads) are dispatched from (see Multi-process Architecture).
  • file_thread: A general process thread for file operations. When you want to do blocking filesystem operations (for example, requesting an icon for a file type, or writing downloaded files to disk), dispatch to this thread.
  • db_thread: A thread for database operations. For example, the cookie service does sqlite operations on this thread. Note that the two major databases (history and web data) don't use this thread yet.
  • safe_browsing_thread

Several components have their own threads:

  • History: The history service object has its own thread. This might be merged with the db_thread above. However, we need to be sure that things happen in the correct order -- for example, that cookies are loaded before history since cookies are needed for the first load, and history initialization is long and will block it.
  • Web data: The web data service is similar to history, but it stores random data for web browsing such as saved passwords, keywords, and installed applications.
  • Proxy service: See net/http/http_proxy_service.cc.
  • Automation proxy: This thread is used to communicate with the UI test program driving the app.

Getting stuff to other threads

PostTask

The lowest level of dispatching to another thread is to use the MessageLoop.PostTask and MessageLoop.PostDelayedTask (see base/message_loop.h). PostTask schedules a task to be run on a particular thread. PostDelayedTask schedules a task to be run after a delay on a particular thread. A task is a simple virtual interface that defines one function: void Run(). To process a task, the message loop eventually calls the Task's Run function, and then deletes the task. Both PostTask and PostDelayedTask take a tracked_objects::Location parameter, which is used for lightweight debugging purposes (counts and primitive profiling of pending and completed tasks can be monitored in a debug build via the url about:objects). Generally the macro value FROM_HERE is the appropriate value to use in this parameter.

Note that new tasks go on the message loop's queue, and any delay that is specified is subject to the operating systems' timer resolutions. This means that under Windows, very small timeouts (under 10ms) will likely not be honored (and will be longer). Using a timeout of 0 in PostDelayedTask is equivalent to calling PostTask, and adds no delay beyond queuing delay. PostTask is also used to do something on the current thread "sometime after the current processing returns to the message loop." Such a continuation on the current thread can be used to assure that other time critical tasks are not starved on the current thread.

The following is an example of a class declaring a specialized task that can be posted, along with an example of of how it could be posted to (in this example) the file thread:

class MyTask : public Task {
public:
virtual void Run() {
DoSomething();
}
};

// Note that threads will be NULL in unit testing mode.
Thread* file_thread = g_browser_process->file_thread();
if (file_thread())
file_thread->message_loop()->PostTask(FROM_HERE, new MyTask);

Runnable methods

It is annoying to have to create small task objects for everything you need to do on another thread. To make it easier, we have a helper functions called NewRunnableMethod (in base/task.h) to assist you. It will automatically construct a templatized Task to call functions on objects.

These functions take an object, a pointer to a function on that object, and optional arguments to the function (overrides allow different number of arguments). The object that PostTask uses must be be a thread-safe reference-counted object. Reference counting ensures that the object invoked on another thread will stay alive until the task completes.

class MyObject : public RefCountedThreadSafe<MyObject> {
public:
void DoSomething(const std;:wstring& name) {
thread_->message_loop()->PostTask(FROM_HERE
NewRunnableMethod(this, &MyObject::DoSomethingOnAnotherThread, name));
}

void DoSomethingOnAnotherThread(const std::wstring& name) {
...
}

private:
Thread* thread_;
};

The arguments in the runnable method will be inferred from the types of the arguments of the destination function. A Tuple (defined in base/tuple.h) is used to store these parameters and dispatch to the final function. Object arguments (integers, etc.) will be copied. Pointer values, but not the object they point to, will be copied, so you probably don't want to pass pointers.

There is a special TupleTraits (in base/tuple.h) specialization defined for reference arguments so that they will be copied In the example above, the name parameter in DoSomethingOnAnotherThread will actually refer to a copy of the original string stored in the tuple (inside the object generated by NewRunnableMethod). If you have an object that needs special copying semantics, you can define your own TupleTraits specialization for it.

Sometimes, you will want to pass reference-counted objects as parameters (be sure to use RefCountedThreadSafe and not plain RefCounted as the base class for these objects). To ensure that the object lives throughout the entire request, the Task generated by NewRunnableMethod must keep a reference to it. This can be done by passing scoped_refptr as the parameter type:

class SomeParamObject : public RefCountedThreadSafe<SomeParamObject> {
...
};

class MyObject : public RefCountedThreadSafe<MyObject> {
public:
void DoSomething() {
scoped_refptr<SomeParamObject> param(new SomeParamObject);
thread_->message_loop()->PostTask(FROM_HERE
NewRunnableMethod(this, &MyObject::DoSomethingOnAnotherThread, param));
}

void DoSomethingOnAnotherThread(scoped_refptr<SomeParamObject> param) {
...
}
};

Scoped factories

Sometimes you will want to do something "later" on your object. You use PostDelayedTask with a timer value and issue it on the current thread. But what happens if your source object's lifetime is managed by another component, and it gets deleted while the task is waiting to fire? This has been the cause of a number of crashes, especially on shutdown.

In these cases, you can use a RevokableStore (in base/revokable_store.h) to ensure that any invokes can not outlive the object they are being invoked on, without using reference counting. A "factory" object will generate special tasks that know about the factory object. When the factory is destroyed, all the tasks will have their internal "revoked" flag set, which will cause them to not dispatch to the original object. By putting the factory as a member of the object being dispatched to, you can get automatic canceling.

class MyObject {
public:
MyObject() : factory_(this) {
}

void DoSomething() {
const int kDelayMS = 100;
MessageLoop::current()->PostDelayedTask(FROM_HERE,
factory_.NewRunnableMethod(&MyObject::DoSomethingLater),
kDelayMS);
}

void DoSomethingLater() {
...
}

private:
ScopedRunnableMethodFactory<MyObject> factory_;
};

Cancelable request

A cancelable request makes it easier to make requests to another thread with that thread returning some data to you asynchronously. Like the revokable store system, it uses objects that track whether the originating object is alive. When the calling object is deleted, the request will be canceled to prevent invalid callbacks.

Like the revokable store system, a user of a cancelable request has an object (here, called a "Consumer") that tracks whether it is alive and will auto-cancel any outstanding requests on deleting.

class MyClass {
void MakeRequest() {
frontend_service->StartRequest(some_input1, some_input2, this,
NewCallback(this, &MyClass:RequestComplete));
}

void RequestComplete(int status) {
...
}

private:
CancelableRequestConsumer consumer_;
};

The consumer also allows you to associate extra data with a request. Use CancelableRequestConsumerT which will allow you to associate arbitrary data with the handle returned by the provider service when you invoke the request. The data will be automatically destroyed when the request is canceled.

A service handling requests inherits from CancelableRequestProvider. This object provides methods for canceling in-flight requests, and will work with the consumers to make sure everything is cleaned up properly on cancel. This frontend service just tracks the request and sends it to a backend service on another thread for actual processing. It would look like this:

class FrontendService : public CancelableRequestProvider {
typedef Callback1::Type RequestCallbackType;

Handle StartRequest(int some_input1, int some_input2,
CallbackConsumer* consumer,
RequestCallbackType* callback) {
scoped_refptr > request(
new CancelableRequest(callback));
AddRequest(request, consumer);

// Send the parameters and the request to the backend thread.
backend_thread_->PostTask(FROM_HERE,
NewRunnableMethod(backend_, & BackendService::DoRequest, request,
some_input1, some_input2), 0);

// The handle will have been set by AddRequest.
return request->handle();
}
};

The backend service runs on another thread. It does processing and forwards the result back to the original caller. It would look like this:

class BackendService : public RefCountedThreadSafe<BackendService> {
void DoRequest(
scoped_refptr< CancelableRequest<Frontend::RequestCallbackType> >
request,
int some_input1, int some_input2) {
if (request->canceled())
return;

... do your processing ...

// Depending on your typedefs, one of these two forms will be more
// convenient:
request->ForwardResult(Tuple1<int>(return_value));

// -- or -- (inferior in this case)
request->ForwardResult(FrontendService::RequestCallbackType::TupleType(
return_value));
}
};