Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(289)

Side by Side Diff: docs/design/threading.md

Issue 2822353002: Port threading design doc to in-tree docs, start a README for design docs in the tree. (Closed)
Patch Set: Fix line wrapping Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « docs/design/README.md ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Threading
2
3 [TOC]
4
5 ## Overview
6
7 Chromium is a very multithreaded product. We try to keep the UI as responsive as
8 possible, and this means not blocking the UI thread with any blocking I/O or
9 other expensive operations. Our approach is to use message passing as the way of
10 communicating between threads. We discourage locking and threadsafe
11 objects. Instead, objects live on only one thread, we pass messages between
12 threads for communication, and we use callback interfaces (implemented by
13 message passing) for most cross-thread requests.
14
15 The `Thread` object is defined in
16 [`base/threading/thread.h`](https://cs.chromium.org/chromium/src/base/threading/ thread.h).
17 In general you should probably use one of the existing threads described below
18 rather than make new ones. We already have a lot of threads that are difficult
19 to keep track of. Each thread has a `MessageLoop` (see
20 [`base/message_loop/message_loop.h`](https://cs.chromium.org/chromium/src/base/m essage_loop/message_loop.h)
21 that processes messages for that thread. You can get the message loop for a
22 thread using the `Thread.message_loop()` function. More details about
23 `MessageLoop` can be found in
24 [Anatomy of Chromium MessageLoop](https://docs.google.com/document/d/1_pJUHO3f3V yRSQjEhKVvUU7NzCyuTCQshZvbWeQiCXU/view#).
25
26 ## Existing threads
27
28 Most threads are managed by the BrowserProcess object, which acts as the service
29 manager for the main "browser" process. By default, everything happens on the UI
30 thread. We have pushed certain classes of processing into these other
31 threads. It has getters for the following threads:
32
33 * **ui_thread**: Main thread where the application starts up.
34 * **io_thread**: This thread is somewhat mis-named. It is the dispatcher thread
35 that handles communication between the browser process and all the
36 sub-processes. It is also where all resource requests (web page loads) are
37 dispatched from (see
38 [Multi-process Architecture](https://www.chromium.org/developers/design-docum ents/multi-process-architecture)).
39 * **file_thread**: A general process thread for file operations. When you want to
40 do blocking filesystem operations (for example, requesting an icon for a file
41 type, or writing downloaded files to disk), dispatch to this thread.
42 * **db_thread**: A thread for database operations. For example, the cookie
43 service does sqlite operations on this thread. Note that the history database
44 doesn't use this thread yet.
45 * **safe_browsing_thread**
46
47 Several components have their own threads:
48
49 * **History**: The history service object has its own thread. This might be
50 merged with the db_thread above. However, we need to be sure that things
51 happen in the correct order -- for example, that cookies are loaded before
52 history since cookies are needed for the first load, and history
53 initialization is long and will block it.
54 * **Proxy service**: See
55 [`net/http/http_proxy_service.cc`](https://cs.chromium.org/chromium/src/net/h ttp/http_proxy_service.cc).
56 * **Automation proxy**: This thread is used to communicate with the UI test
57 program driving the app.
58
59 ## Keeping the browser responsive
60
61 As hinted in the overview, we avoid doing any blocking I/O on the UI thread to
62 keep the UI responsive. Less apparent is that we also need to avoid blocking
63 I/O on the IO thread. The reason is that if we block it for an expensive
64 operation, say disk access, then IPC messages don't get processed. The effect
65 is that the user can't interact with a page. Note that asynchronous/overlapped
66 I/O are fine.
67
68 Another thing to watch out for is to not block threads on one another. Locks
69 should only be used to swap in a shared data structure that can be accessed on
70 multiple threads. If one thread updates it based on expensive computation or
71 through disk access, then that slow work should be done without holding on to
72 the lock. Only when the result is available should the lock be used to swap in
73 the new data. An example of this is in PluginList::LoadPlugins
74 ([`content/common/plugin_list.cc`](https://cs.chromium.org/chromium/src/content/ common/plugin_list.cc). If
75 you must use locks,
76 [here](https://www.chromium.org/developers/lock-and-condition-variable)
77 are some best practices and pitfalls to avoid.
78
79 In order to write non-blocking code, many APIs in Chromium are
80 asynchronous. Usually this means that they either need to be executed on a
81 particular thread and will return results via a custom delegate interface, or
82 they take a `base::Callback<>` object that is called when the requested
83 operation is completed. Executing work on a specific thread is covered in the
84 PostTask section below.
85
86 ## Getting stuff to other threads
87
88 ### `base::Callback<>`, Async APIs and Currying
89
90
91 A `base::Callback<>` (see the docs in
92 [`base/callback.h`](https://cs.chromium.org/chromium/src/base/callback.h) is
93 a templated class with a `Run()` method. It is a generalization of a function
94 pointer and is created by a call to `base::Bind`. Async APIs often will take a
95 `base::Callback<>` as a means to asynchronously return the results of an
96 operation. Here is an example of a hypothetical FileRead API.
97
98 void ReadToString(const std::string& filename, const base::Callback<void(con st std::string&)>& on_read);
99
100 void DisplayString(const std::string& result) {
101 LOG(INFO) << result;
102 }
103
104 void SomeFunc(const std::string& file) {
105 ReadToString(file, base::Bind(&DisplayString));
106 };
107
108 In the example above, `base::Bind` takes the function pointer `&DisplayString`
109 and turns it into a `base::Callback<void(const std::string& result)>`. The type
110 of the generated `base::Callback<>` is inferred from the arguments. Why not
111 just pass the function pointer directly? The reason is `base::Bind` allows the
112 caller to adapt function interfaces and/or attach extra context
113 via [Currying](http://en.wikipedia.org/wiki/Currying). For instance, if we had
114 a utility function `DisplayStringWithPrefix` that took an extra argument with
115 the prefix, we use `base::Bind` to adapt the interface as follows.
116
117 void DisplayStringWithPrefix(const std::string& prefix, const std::string& r esult) {
118 LOG(INFO) << prefix << result;
119 }
120
121 void AnotherFunc(const std::string& file) {
122 ReadToString(file, base::Bind(&DisplayStringWithPrefix, "MyPrefix: "));
123 };
124
125 This can be used in lieu of creating an adapter functions a small classes that
126 holds prefix as a member variable. Notice also that the `"MyPrefix: "` argument
127 is actually a `const char*`, while `DisplayStringWithPrefix` actually wants a
128 `const std::string&`. Like normal function dispatch, `base::Bind`, will coerce
129 parameters types if possible.
130
131 See [How arguments are handled by base::Bind()](#how_arguments_are_handled)
132 below for more details about argument storage, copying, and special handling of
133 references.
134
135 ### PostTask
136
137 The lowest level of dispatching to another thread is to use the
138 `MessageLoop.PostTask` and `MessageLoop.PostDelayedTask`
139 (see
140 [`base/message_loop/message_loop.h`](https://cs.chromium.org/chromium/src/base/m essage_loop/message_loop.h)).
141 PostTask schedules a task to be run on a particular thread. A task is defined
142 as a `base::Closure`, which is a typedef for a
143 `base::Callback<void(void)>`. `PostDelayedTask` schedules a task to be run after
144 a delay on a particular thread. A task is represented by the `base::Closure`
145 typedef, which contains a `Run()` function, and is created by calling
146 `base::Bind()`. To process a task, the message loop eventually calls
147 `base::Closure`'s `Run` function, and then drops the reference to the task
148 object. Both `PostTask` and `PostDelayedTask` take a `tracked_objects::Location`
149 parameter, which is used for lightweight debugging purposes (counts and
150 primitive profiling of pending and completed tasks can be monitored in a debug
151 build via the url about:objects). Generally the macro value `FROM_HERE` is the
152 appropriate value to use in this parameter.
153
154 Note that new tasks go on the message loop's queue, and any delay that is
155 specified is subject to the operating system's timer resolutions. This means
156 that under Windows, very small timeouts (under 10ms) will likely not be honored
157 (and will be longer). Using a timeout of 0 in `PostDelayedTask` is equivalent to
158 calling `PostTask`, and adds no delay beyond queuing delay. `PostTask` is also
159 used to do something on the current thread "sometime after the current
160 processing returns to the message loop." Such a continuation on the current
161 thread can be used to assure that other time critical tasks are not starved on
162 this thread.
163
164 The following is an example of a creating a task for a function and posting it
165 to another thread (in this example, the file thread):
166
167 void WriteToFile(const std::string& filename, const std::string& data);
168 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
169 base::Bind(&WriteToFile, "foo.txt", "hello world!")) ;
170
171 You should always use `BrowserThread` to post tasks between threads. Never
172 cache `MessageLoop` pointers as it can cause bugs such as the pointers being
173 deleted while you're still holding on to them. More information can be
174 found
175 [here](https://www.chromium.org/developers/design-documents/threading/suble-thre ading-bugs-and-patterns-to-avoid-them).
176
177
178 ### base::Bind() and class methods.
179
180 The `base::Bind()` API also supports invoking class methods as well. The syntax
181 is very similar to calling `base::Bind()` on a function, except the first
182 argument should be the object the method belongs to. By default, the object that
183 `PostTask` uses must be a thread-safe reference-counted object. Reference
184 counting ensures that the object invoked on another thread will stay alive until
185 the task completes.
186
187 class MyObject : public base::RefCountedThreadSafe<MyObject> {
188 public:
189 void DoSomething(const std::string16& name) {
190 thread_->message_loop()->PostTask(
191 FROM_HERE, base::Bind(&MyObject::DoSomethingOnAnotherThread, this, na me));
192 }
193
194 void DoSomethingOnAnotherThread(const std::string16& name) {
195 ...
196 }
197 private:
198 // Always good form to make the destructor private so that only RefCounted
199 // ThreadSafe can access it.
200 // This avoids bugs with double deletes.
201 friend class base::RefCountedThreadSafe<MyObject>;
202
203 ~MyObject();
204 Thread* thread_;
205 };
206
207 If you have external synchronization structures that can completely ensure that
208 an object will always be alive while the task is waiting to execute, you can
209 wrap the object pointer with `base::Unretained()` when calling `base::Bind()` to
210 disable the refcounting. This will also allow using `base::Bind()` on classes
211 that are not refcounted. Be careful when doing this!
212
213
214
215 ### How arguments are handled by `base::Bind()`
216 <a id="how_arguments_are_handled"></a>
217
218 The arguments given to `base::Bind()` are copied into an internal
219 `InvokerStorage` structure object (defined in
220 [`base/bind_internal.h`](http://cs.chromium.org/chromium/src/base/bind_internal. h).
221 When the function is finally executed, it will see copies of the arguments. Thi s is important if your target function or method takes a const reference; the
222 reference will be to a copy of the argument. If you need a reference to the
223 original argument, you can wrap the argument with `base::ConstRef()`. Use this
224 carefully as it is likely dangerous if target of the reference cannot be
225 guaranteed to live past when the task is executed. In particular, it is almost
226 never safe to use `base::ConstRef()` to a variable on the stack unless you can
227 guarantee the stack frame will not be invalidated until the asynchronous task
228 finishes.
229
230 Sometimes, you will want to pass reference-counted objects as parameters (be
231 sure to use `RefCountedThreadSafe` and not plain `RefCounted` as the base class
232 for these objects). To ensure that the object lives throughout the entire
233 request, the Closure generated by `base::Bind` must keep a reference to it. This
234 can be done by passing scoped_refptr as the parameter type, or by wrapping the
235 raw pointer with `make_scoped_refptr()`:
236
237 class SomeParamObject : public base::RefCountedThreadSafe<SomeParamObject> {
238 ...
239 };
240
241 class MyObject : public base::RefCountedThreadSafe<MyObject> {
242 public:
243 void DoSomething() {
244 scoped_refptr<SomeParamObject> param(new SomeParamObject);
245 thread_->message_loop()->PostTask(FROM_HERE
246 base::Bind(&MyObject::DoSomethingOnAnotherThread, this, param));
247 }
248 void DoSomething2() {
249 SomeParamObject* param = new SomeParamObject;
250 thread_->message_loop()->PostTask(FROM_HERE
251 base::Bind(&MyObject::DoSomethingOnAnotherThread, this,
252 make_scoped_refptr(param)));
253 }
254 // Note how this takes a raw pointer. The important part is that
255 // base::Bind() was passed a scoped_refptr; using a scoped_refptr
256 // here would result in an extra AddRef()/Release() pair.
257 void DoSomethingOnAnotherThread(SomeParamObject* param) {
258 ...
259 }
260 };
261
262 If you want to pass the object without taking a reference on it, wrap the
263 argument with `base::Unretained()`. Again, using this means there are external
264 guarantees on the lifetime of the object, so tread carefully!
265
266 If your object has a non-trivial destructor that needs to run on a specific
267 thread, you can use the following trait. This is needed since timing races could
268 lead to a task completing execution before the code that posted it has unwound
269 the stack.
270
271 class MyObject : public base::RefCountedThreadSafe<MyObject, BrowserThread:: DeleteOnIOThread> {
272
273 ## Callback cancellation
274
275 There are 2 major reasons to cancel a task (in the form of a Callback):
276 * You want to do something later on your object, but at the time your callback
277 runs, your object may have been destroyed.
278 * When input changes (e.g. user input), old tasks become unnecessary. For
279 performance considerations, you should cancel them.
280 See following about different approaches for cancellation.
281
282 ### Important notes about cancellation
283
284 It's dangerous to cancel a task with owned parameters. See the following
285 example. (The example uses `base::WeakPtr` for cancellation, but the problem
286 applies to all approaches).
287
288 class MyClass {
289 public:
290 // Owns |p|.
291 void DoSomething(AnotherClass* p) {
292 ...
293 }
294 WeakPtr<MyClass> AsWeakPtr() {
295 return weak_factory_.GetWeakPtr();
296 }
297 private:
298 base::WeakPtrFactory<MyClass> weak_factory_;
299 };
300
301 ...
302 Closure cancelable_closure = Bind(&MyClass::DoSomething, object->AsWeakPtr() , p);
303 Callback<void(AnotherClass*)> cancelable_callback = Bind(&MyClass::DoSomethi ng, object->AsWeakPtr());
304 ...
305
306 void FunctionRunLater(const Closure& cancelable_closure,
307 const Callback<void(AnotherClass*)>& cancelable_callba ck) {
308 ...
309 // Leak memory!
310 cancelable_closure.Run();
311 cancelable_callback.Run(p);
312 }
313
314 In `FunctionRunLater`, both `Run()` calls will leak `p` when object is already
315 destructed. Using `scoped_ptr` can fix the bug:
316
317 class MyClass {
318 public:
319 void DoSomething(scoped_ptr<AnotherClass> p) {
320 ...
321 }
322 ...
323 };
324
325 ### base::WeakPtr and Cancellation __[NOT THREAD SAFE]__
326
327 You can use a `base::WeakPtr` and `base::WeakPtrFactory`
328 (in
329 [base/memory/weak_ptr.h](https://cs.chromium.org/chromium/src/base/memory/weak_p tr.h))
330 to ensure that any invokes can not outlive the object they are being invoked on,
331 without using reference counting. The `base::Bind` mechanism has special
332 understanding for `base::WeakPtr` that will disable the task's execution if the
333 `base::WeakPtr` has been invalidated. The `base::WeakPtrFactory` object can be
334 used to generate `base::WeakPtr` instances that know about the factory
335 object. When the factory is destroyed, all the `base::WeakPtr` will have their
336 internal "invalidated" flag set, which will make any tasks bound to them to not
337 dispatch. By putting the factory as a member of the object being dispatched to,
338 you can get automatic cancellation.
339
340 __NOTE__: This only works when the task is posted to the same thread. Currently
341 there is not a general solution that works for tasks posted to other
342 threads. See
343 the [next section about CancelableTaskTracker](#cancelable_task_tracker) for an
344 alternative solution.
345
346 class MyObject {
347 public:
348 MyObject() : weak_factory_(this) {}
349
350 void DoSomething() {
351 const int kDelayMS = 100;
352 MessageLoop::current()->PostDelayedTask(FROM_HERE,
353 base::Bind(&MyObject::DoSomethingLater, weak_factory_.GetWeakPtr()),
354 kDelayMS);
355 }
356
357 void DoSomethingLater() {
358 ...
359 }
360
361 private:
362 base::WeakPtrFactory<MyObject> weak_factory_;
363 };
364
365 ### CancelableTaskTracker
366 <a id="cancelable_task_tracker"></a>
367
368 While `base::WeakPtr` is very helpful to cancel a task, it is not thread safe so
369 can not be used to cancel tasks running on another thread. This is sometimes a
370 performance critical requirement. E.g. We need to cancel database lookup task on
371 DB thread when user changes inputed text. In this kind of situation
372 `CancelableTaskTracker` is appropriate.
373
374 With `CancelableTaskTracker` you can cancel a single task with returned
375 `TaskId`. This is another reason to use `CancelableTaskTracker` instead of
376 `base::WeakPtr`, even in a single thread context.
377
378 `CancelableTaskTracker` has 2 `Post` methods doing the same thing as the ones in
379 `base::TaskRunner`, with additional cancellation support.
380
381 class UserInputHandler : public base::RefCountedThreadSafe<UserInputHandler> {
382 // Runs on UI thread.
383 void OnUserInput(Input input) {
384 CancelPreviousTask();
385 DBResult* result = new DBResult();
386 task_id_ = tracker_->PostTaskAndReply(
387 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB).get() ,
388 FROM_HERE,
389 base::Bind(&LookupHistoryOnDBThread, this, input, result),
390 base::Bind(&ShowHistoryOnUIThread, this, base::Owned(result)));
391 }
392
393 void CancelPreviousTask() {
394 tracker_->TryCancel(task_id_);
395 }
396
397 ...
398
399 private:
400 CancelableTaskTracker tracker_; // Cancels all pending tasks while destru ction.
401 CancelableTaskTracker::TaskId task_id_;
402 ...
403 };
404
405 Since task runs on other threads, there's no guarantee it can be successfully
406 canceled.
407
408 When `TryCancel()` is called:
409
410 * If neither task nor reply has started running, both will be canceled.
411 * If task is already running or has finished running, reply will be canceled.
412 * If reply is running or has finished running, cancelation is a noop.
413
414 Like `base::WeakPtrFactory`, `CancelableTaskTracker` will cancel all tasks on
415 destruction.
416
417 ### Cancelable request __(DEPRECATED)__
418
419 Note. Cancelable request is deprecated. Please do not use it in new code. For
420 canceling tasks running on the same thread, use WeakPtr. For canceling tasks
421 running on a different thread, use `CancelableTaskTracker`.
422
423 A cancelable request makes it easier to make requests to another thread with
424 that thread returning some data to you asynchronously. Like the revokable store
425 system, it uses objects that track whether the originating object is alive. When
426 the calling object is deleted, the request will be canceled to prevent invalid
427 callbacks.
428
429 Like the revokable store system, a user of a cancelable request has
430 an object (here, called a _Consumer_) that tracks whether it is alive and will
431 auto-cancel any outstanding requests on deleting.
432
433 class MyClass {
434 void MakeRequest() {
435 frontend_service->StartRequest(some_input1, some_input2, this,
436 // Use base::Unretained(this) if this may cause a refcount cycle.
437 base::Bind(&MyClass:RequestComplete, this));
438 }
439 void RequestComplete(int status) {
440 ...
441 }
442
443 private:
444 CancelableRequestConsumer consumer_;
445 };
446
447 Note that the `MyClass::RequestComplete`, is bounded with
448 `base::Unretained(this)` here.
449
450 The consumer also allows you to associate extra data with a request. Use
451 `CancelableRequestConsumer` which will allow you to associate arbitrary data
452 with the handle returned by the provider service when you invoke the
453 request. The data will be automatically destroyed when the request is canceled.
454
455 A service handling requests inherits from `CancelableRequestProvider`. This
456 object provides methods for canceling in-flight requests, and will work with the
457 consumers to make sure everything is cleaned up properly on cancel. This
458 frontend service just tracks the request and sends it to a backend service on
459 another thread for actual processing. It would look like this:
460
461 class FrontendService : public CancelableRequestProvider {
462 typedef base::Callback<void(int)> RequestCallbackType;
463
464 Handle StartRequest(int some_input1, int some_input2,
465 CallbackConsumer* consumer,
466 const RequestCallbackType& callback) {
467 scoped_refptr<CancelableRequest<FrontendService::RequestCallbackType>>
468 request(new CancelableRequest(callback));
469 AddRequest(request, consumer);
470
471 // Send the parameters and the request to the backend thread.
472 backend_thread_->PostTask(FROM_HERE,
473 base::Bind(&BackendService::DoRequest, backend_, request,
474 some_input1, some_input2), 0);
475 // The handle will have been set by AddRequest.
476 return request->handle();
477 }
478 };
479
480 The backend service runs on another thread. It does processing and forwards the
481 result back to the original caller. It would look like this:
482
483 class BackendService : public base::RefCountedThreadSafe<BackendService> {
484 void DoRequest(
485 scoped_refptr<CancelableRequest<FrontendService::RequestCallbackType>>
486 request,
487 int some_input1, int some_input2) {
488 if (request->canceled())
489 return;
490
491 ... do your processing ...
492
493 // Execute ForwardResult() like you would do Run() on the base::Callback <>.
494 request->ForwardResult(return_value);
495 }
496 };
OLDNEW
« no previous file with comments | « docs/design/README.md ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698