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