browser_thread_impl.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/browser_thread_impl.h"
6
7#include <string>
8
9#include "base/atomicops.h"
10#include "base/bind.h"
11#include "base/compiler_specific.h"
12#include "base/lazy_instance.h"
13#include "base/message_loop.h"
14#include "base/message_loop_proxy.h"
15#include "base/threading/sequenced_worker_pool.h"
16#include "base/threading/thread_restrictions.h"
17#include "content/public/browser/browser_thread_delegate.h"
18
19namespace content {
20
21namespace {
22
23// Friendly names for the well-known threads.
24static const char* g_browser_thread_names[BrowserThread::ID_COUNT] = {
25  "",  // UI (name assembled in browser_main.cc).
26  "Chrome_DBThread",  // DB
27  "Chrome_WebKitThread",  // WEBKIT_DEPRECATED
28  "Chrome_FileThread",  // FILE
29  "Chrome_FileUserBlockingThread",  // FILE_USER_BLOCKING
30  "Chrome_ProcessLauncherThread",  // PROCESS_LAUNCHER
31  "Chrome_CacheThread",  // CACHE
32  "Chrome_IOThread",  // IO
33};
34
35struct BrowserThreadGlobals {
36  BrowserThreadGlobals()
37      : blocking_pool(new base::SequencedWorkerPool(3, "BrowserBlocking")) {
38    memset(threads, 0, BrowserThread::ID_COUNT * sizeof(threads[0]));
39    memset(thread_delegates, 0,
40           BrowserThread::ID_COUNT * sizeof(thread_delegates[0]));
41  }
42
43  // This lock protects |threads|. Do not read or modify that array
44  // without holding this lock. Do not block while holding this lock.
45  base::Lock lock;
46
47  // This array is protected by |lock|. The threads are not owned by this
48  // array. Typically, the threads are owned on the UI thread by
49  // BrowserMainLoop. BrowserThreadImpl objects remove themselves from this
50  // array upon destruction.
51  BrowserThreadImpl* threads[BrowserThread::ID_COUNT];
52
53  // Only atomic operations are used on this array. The delegates are not owned
54  // by this array, rather by whoever calls BrowserThread::SetDelegate.
55  BrowserThreadDelegate* thread_delegates[BrowserThread::ID_COUNT];
56
57  const scoped_refptr<base::SequencedWorkerPool> blocking_pool;
58};
59
60base::LazyInstance<BrowserThreadGlobals>::Leaky
61    g_globals = LAZY_INSTANCE_INITIALIZER;
62
63}  // namespace
64
65BrowserThreadImpl::BrowserThreadImpl(ID identifier)
66    : Thread(g_browser_thread_names[identifier]),
67      identifier_(identifier) {
68  Initialize();
69}
70
71BrowserThreadImpl::BrowserThreadImpl(ID identifier,
72                                     base::MessageLoop* message_loop)
73    : Thread(message_loop->thread_name().c_str()), identifier_(identifier) {
74  set_message_loop(message_loop);
75  Initialize();
76}
77
78// static
79void BrowserThreadImpl::ShutdownThreadPool() {
80  // The goal is to make it impossible for chrome to 'infinite loop' during
81  // shutdown, but to reasonably expect that all BLOCKING_SHUTDOWN tasks queued
82  // during shutdown get run. There's nothing particularly scientific about the
83  // number chosen.
84  const int kMaxNewShutdownBlockingTasks = 1000;
85  BrowserThreadGlobals& globals = g_globals.Get();
86  globals.blocking_pool->Shutdown(kMaxNewShutdownBlockingTasks);
87}
88
89// static
90void BrowserThreadImpl::FlushThreadPoolHelper() {
91  // We don't want to create a pool if none exists.
92  if (g_globals == NULL)
93    return;
94  g_globals.Get().blocking_pool->FlushForTesting();
95}
96
97void BrowserThreadImpl::Init() {
98  BrowserThreadGlobals& globals = g_globals.Get();
99
100  using base::subtle::AtomicWord;
101  AtomicWord* storage =
102      reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]);
103  AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage);
104  BrowserThreadDelegate* delegate =
105      reinterpret_cast<BrowserThreadDelegate*>(stored_pointer);
106  if (delegate)
107    delegate->Init();
108}
109
110// We disable optimizations for this block of functions so the compiler doesn't
111// merge them all together.
112MSVC_DISABLE_OPTIMIZE()
113MSVC_PUSH_DISABLE_WARNING(4748)
114
115NOINLINE void BrowserThreadImpl::UIThreadRun(base::MessageLoop* message_loop) {
116  volatile int line_number = __LINE__;
117  Thread::Run(message_loop);
118  CHECK_GT(line_number, 0);
119}
120
121NOINLINE void BrowserThreadImpl::DBThreadRun(base::MessageLoop* message_loop) {
122  volatile int line_number = __LINE__;
123  Thread::Run(message_loop);
124  CHECK_GT(line_number, 0);
125}
126
127NOINLINE void BrowserThreadImpl::WebKitThreadRun(
128    base::MessageLoop* message_loop) {
129  volatile int line_number = __LINE__;
130  Thread::Run(message_loop);
131  CHECK_GT(line_number, 0);
132}
133
134NOINLINE void BrowserThreadImpl::FileThreadRun(
135    base::MessageLoop* message_loop) {
136  volatile int line_number = __LINE__;
137  Thread::Run(message_loop);
138  CHECK_GT(line_number, 0);
139}
140
141NOINLINE void BrowserThreadImpl::FileUserBlockingThreadRun(
142    base::MessageLoop* message_loop) {
143  volatile int line_number = __LINE__;
144  Thread::Run(message_loop);
145  CHECK_GT(line_number, 0);
146}
147
148NOINLINE void BrowserThreadImpl::ProcessLauncherThreadRun(
149    base::MessageLoop* message_loop) {
150  volatile int line_number = __LINE__;
151  Thread::Run(message_loop);
152  CHECK_GT(line_number, 0);
153}
154
155NOINLINE void BrowserThreadImpl::CacheThreadRun(
156    base::MessageLoop* message_loop) {
157  volatile int line_number = __LINE__;
158  Thread::Run(message_loop);
159  CHECK_GT(line_number, 0);
160}
161
162NOINLINE void BrowserThreadImpl::IOThreadRun(base::MessageLoop* message_loop) {
163  volatile int line_number = __LINE__;
164  Thread::Run(message_loop);
165  CHECK_GT(line_number, 0);
166}
167
168MSVC_POP_WARNING()
169MSVC_ENABLE_OPTIMIZE();
170
171void BrowserThreadImpl::Run(base::MessageLoop* message_loop) {
172  BrowserThread::ID thread_id;
173  if (!GetCurrentThreadIdentifier(&thread_id))
174    return Thread::Run(message_loop);
175
176  switch (thread_id) {
177    case BrowserThread::UI:
178      return UIThreadRun(message_loop);
179    case BrowserThread::DB:
180      return DBThreadRun(message_loop);
181    case BrowserThread::WEBKIT_DEPRECATED:
182      return WebKitThreadRun(message_loop);
183    case BrowserThread::FILE:
184      return FileThreadRun(message_loop);
185    case BrowserThread::FILE_USER_BLOCKING:
186      return FileUserBlockingThreadRun(message_loop);
187    case BrowserThread::PROCESS_LAUNCHER:
188      return ProcessLauncherThreadRun(message_loop);
189    case BrowserThread::CACHE:
190      return CacheThreadRun(message_loop);
191    case BrowserThread::IO:
192      return IOThreadRun(message_loop);
193    case BrowserThread::ID_COUNT:
194      CHECK(false);  // This shouldn't actually be reached!
195      break;
196  }
197  Thread::Run(message_loop);
198}
199
200void BrowserThreadImpl::CleanUp() {
201  BrowserThreadGlobals& globals = g_globals.Get();
202
203  using base::subtle::AtomicWord;
204  AtomicWord* storage =
205      reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]);
206  AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage);
207  BrowserThreadDelegate* delegate =
208      reinterpret_cast<BrowserThreadDelegate*>(stored_pointer);
209
210  if (delegate)
211    delegate->CleanUp();
212}
213
214void BrowserThreadImpl::Initialize() {
215  BrowserThreadGlobals& globals = g_globals.Get();
216
217  base::AutoLock lock(globals.lock);
218  DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT);
219  DCHECK(globals.threads[identifier_] == NULL);
220  globals.threads[identifier_] = this;
221}
222
223BrowserThreadImpl::~BrowserThreadImpl() {
224  // All Thread subclasses must call Stop() in the destructor. This is
225  // doubly important here as various bits of code check they are on
226  // the right BrowserThread.
227  Stop();
228
229  BrowserThreadGlobals& globals = g_globals.Get();
230  base::AutoLock lock(globals.lock);
231  globals.threads[identifier_] = NULL;
232#ifndef NDEBUG
233  // Double check that the threads are ordered correctly in the enumeration.
234  for (int i = identifier_ + 1; i < ID_COUNT; ++i) {
235    DCHECK(!globals.threads[i]) <<
236        "Threads must be listed in the reverse order that they die";
237  }
238#endif
239}
240
241// static
242bool BrowserThreadImpl::PostTaskHelper(
243    BrowserThread::ID identifier,
244    const tracked_objects::Location& from_here,
245    const base::Closure& task,
246    base::TimeDelta delay,
247    bool nestable) {
248  DCHECK(identifier >= 0 && identifier < ID_COUNT);
249  // Optimization: to avoid unnecessary locks, we listed the ID enumeration in
250  // order of lifetime.  So no need to lock if we know that the target thread
251  // outlives current thread.
252  // Note: since the array is so small, ok to loop instead of creating a map,
253  // which would require a lock because std::map isn't thread safe, defeating
254  // the whole purpose of this optimization.
255  BrowserThread::ID current_thread;
256  bool target_thread_outlives_current =
257      GetCurrentThreadIdentifier(&current_thread) &&
258      current_thread >= identifier;
259
260  BrowserThreadGlobals& globals = g_globals.Get();
261  if (!target_thread_outlives_current)
262    globals.lock.Acquire();
263
264  base::MessageLoop* message_loop =
265      globals.threads[identifier] ? globals.threads[identifier]->message_loop()
266                                  : NULL;
267  if (message_loop) {
268    if (nestable) {
269      message_loop->PostDelayedTask(from_here, task, delay);
270    } else {
271      message_loop->PostNonNestableDelayedTask(from_here, task, delay);
272    }
273  }
274
275  if (!target_thread_outlives_current)
276    globals.lock.Release();
277
278  return !!message_loop;
279}
280
281// An implementation of MessageLoopProxy to be used in conjunction
282// with BrowserThread.
283class BrowserThreadMessageLoopProxy : public base::MessageLoopProxy {
284 public:
285  explicit BrowserThreadMessageLoopProxy(BrowserThread::ID identifier)
286      : id_(identifier) {
287  }
288
289  // MessageLoopProxy implementation.
290  virtual bool PostDelayedTask(
291      const tracked_objects::Location& from_here,
292      const base::Closure& task, base::TimeDelta delay) OVERRIDE {
293    return BrowserThread::PostDelayedTask(id_, from_here, task, delay);
294  }
295
296  virtual bool PostNonNestableDelayedTask(
297      const tracked_objects::Location& from_here,
298      const base::Closure& task,
299      base::TimeDelta delay) OVERRIDE {
300    return BrowserThread::PostNonNestableDelayedTask(id_, from_here, task,
301                                                     delay);
302  }
303
304  virtual bool RunsTasksOnCurrentThread() const OVERRIDE {
305    return BrowserThread::CurrentlyOn(id_);
306  }
307
308 protected:
309  virtual ~BrowserThreadMessageLoopProxy() {}
310
311 private:
312  BrowserThread::ID id_;
313  DISALLOW_COPY_AND_ASSIGN(BrowserThreadMessageLoopProxy);
314};
315
316// static
317bool BrowserThread::PostBlockingPoolTask(
318    const tracked_objects::Location& from_here,
319    const base::Closure& task) {
320  return g_globals.Get().blocking_pool->PostWorkerTask(from_here, task);
321}
322
323bool BrowserThread::PostBlockingPoolTaskAndReply(
324    const tracked_objects::Location& from_here,
325    const base::Closure& task,
326    const base::Closure& reply) {
327  return g_globals.Get().blocking_pool->PostTaskAndReply(
328      from_here, task, reply);
329}
330
331// static
332bool BrowserThread::PostBlockingPoolSequencedTask(
333    const std::string& sequence_token_name,
334    const tracked_objects::Location& from_here,
335    const base::Closure& task) {
336  return g_globals.Get().blocking_pool->PostNamedSequencedWorkerTask(
337      sequence_token_name, from_here, task);
338}
339
340// static
341base::SequencedWorkerPool* BrowserThread::GetBlockingPool() {
342  return g_globals.Get().blocking_pool;
343}
344
345// static
346bool BrowserThread::IsWellKnownThread(ID identifier) {
347  if (g_globals == NULL)
348    return false;
349
350  BrowserThreadGlobals& globals = g_globals.Get();
351  base::AutoLock lock(globals.lock);
352  return (identifier >= 0 && identifier < ID_COUNT &&
353          globals.threads[identifier]);
354}
355
356// static
357bool BrowserThread::CurrentlyOn(ID identifier) {
358  // We shouldn't use MessageLoop::current() since it uses LazyInstance which
359  // may be deleted by ~AtExitManager when a WorkerPool thread calls this
360  // function.
361  // http://crbug.com/63678
362  base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
363  BrowserThreadGlobals& globals = g_globals.Get();
364  base::AutoLock lock(globals.lock);
365  DCHECK(identifier >= 0 && identifier < ID_COUNT);
366  return globals.threads[identifier] &&
367         globals.threads[identifier]->message_loop() ==
368             base::MessageLoop::current();
369}
370
371// static
372bool BrowserThread::IsMessageLoopValid(ID identifier) {
373  if (g_globals == NULL)
374    return false;
375
376  BrowserThreadGlobals& globals = g_globals.Get();
377  base::AutoLock lock(globals.lock);
378  DCHECK(identifier >= 0 && identifier < ID_COUNT);
379  return globals.threads[identifier] &&
380         globals.threads[identifier]->message_loop();
381}
382
383// static
384bool BrowserThread::PostTask(ID identifier,
385                             const tracked_objects::Location& from_here,
386                             const base::Closure& task) {
387  return BrowserThreadImpl::PostTaskHelper(
388      identifier, from_here, task, base::TimeDelta(), true);
389}
390
391// static
392bool BrowserThread::PostDelayedTask(ID identifier,
393                                    const tracked_objects::Location& from_here,
394                                    const base::Closure& task,
395                                    base::TimeDelta delay) {
396  return BrowserThreadImpl::PostTaskHelper(
397      identifier, from_here, task, delay, true);
398}
399
400// static
401bool BrowserThread::PostNonNestableTask(
402    ID identifier,
403    const tracked_objects::Location& from_here,
404    const base::Closure& task) {
405  return BrowserThreadImpl::PostTaskHelper(
406      identifier, from_here, task, base::TimeDelta(), false);
407}
408
409// static
410bool BrowserThread::PostNonNestableDelayedTask(
411    ID identifier,
412    const tracked_objects::Location& from_here,
413    const base::Closure& task,
414    base::TimeDelta delay) {
415  return BrowserThreadImpl::PostTaskHelper(
416      identifier, from_here, task, delay, false);
417}
418
419// static
420bool BrowserThread::PostTaskAndReply(
421    ID identifier,
422    const tracked_objects::Location& from_here,
423    const base::Closure& task,
424    const base::Closure& reply) {
425  return GetMessageLoopProxyForThread(identifier)->PostTaskAndReply(from_here,
426                                                                    task,
427                                                                    reply);
428}
429
430// static
431bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) {
432  if (g_globals == NULL)
433    return false;
434
435  // We shouldn't use MessageLoop::current() since it uses LazyInstance which
436  // may be deleted by ~AtExitManager when a WorkerPool thread calls this
437  // function.
438  // http://crbug.com/63678
439  base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
440  base::MessageLoop* cur_message_loop = base::MessageLoop::current();
441  BrowserThreadGlobals& globals = g_globals.Get();
442  for (int i = 0; i < ID_COUNT; ++i) {
443    if (globals.threads[i] &&
444        globals.threads[i]->message_loop() == cur_message_loop) {
445      *identifier = globals.threads[i]->identifier_;
446      return true;
447    }
448  }
449
450  return false;
451}
452
453// static
454scoped_refptr<base::MessageLoopProxy>
455BrowserThread::GetMessageLoopProxyForThread(ID identifier) {
456  return make_scoped_refptr(new BrowserThreadMessageLoopProxy(identifier));
457}
458
459// static
460MessageLoop* BrowserThread::UnsafeGetMessageLoopForThread(ID identifier) {
461  if (g_globals == NULL)
462    return NULL;
463
464  BrowserThreadGlobals& globals = g_globals.Get();
465  base::AutoLock lock(globals.lock);
466  base::Thread* thread = globals.threads[identifier];
467  DCHECK(thread);
468  base::MessageLoop* loop = thread->message_loop();
469  return loop;
470}
471
472// static
473void BrowserThread::SetDelegate(ID identifier,
474                                BrowserThreadDelegate* delegate) {
475  using base::subtle::AtomicWord;
476  BrowserThreadGlobals& globals = g_globals.Get();
477  AtomicWord* storage = reinterpret_cast<AtomicWord*>(
478      &globals.thread_delegates[identifier]);
479  AtomicWord old_pointer = base::subtle::NoBarrier_AtomicExchange(
480      storage, reinterpret_cast<AtomicWord>(delegate));
481
482  // This catches registration when previously registered.
483  DCHECK(!delegate || !old_pointer);
484}
485
486}  // namespace content
487