| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "content/browser/browser_thread_impl.h" | 
 |  | 
 | #include <string> | 
 | #include <utility> | 
 |  | 
 | #include "base/atomicops.h" | 
 | #include "base/bind.h" | 
 | #include "base/compiler_specific.h" | 
 | #include "base/lazy_instance.h" | 
 | #include "base/logging.h" | 
 | #include "base/macros.h" | 
 | #include "base/message_loop/message_loop.h" | 
 | #include "base/run_loop.h" | 
 | #include "base/synchronization/waitable_event.h" | 
 | #include "base/threading/platform_thread.h" | 
 | #include "build/build_config.h" | 
 | #include "content/public/browser/browser_thread_delegate.h" | 
 | #include "content/public/browser/content_browser_client.h" | 
 | #include "net/disk_cache/simple/simple_backend_impl.h" | 
 |  | 
 | #if defined(OS_ANDROID) | 
 | #include "base/android/jni_android.h" | 
 | #endif | 
 |  | 
 | namespace content { | 
 |  | 
 | namespace { | 
 |  | 
 | // Friendly names for the well-known threads. | 
 | static const char* const g_browser_thread_names[BrowserThread::ID_COUNT] = { | 
 |   "",  // UI (name assembled in browser_main.cc). | 
 |   "Chrome_DBThread",  // DB | 
 |   "Chrome_FileThread",  // FILE | 
 |   "Chrome_FileUserBlockingThread",  // FILE_USER_BLOCKING | 
 |   "Chrome_ProcessLauncherThread",  // PROCESS_LAUNCHER | 
 |   "Chrome_CacheThread",  // CACHE | 
 |   "Chrome_IOThread",  // IO | 
 | }; | 
 |  | 
 | static const char* GetThreadName(BrowserThread::ID thread) { | 
 |   if (BrowserThread::UI < thread && thread < BrowserThread::ID_COUNT) | 
 |     return g_browser_thread_names[thread]; | 
 |   if (thread == BrowserThread::UI) | 
 |     return "Chrome_UIThread"; | 
 |   return "Unknown Thread"; | 
 | } | 
 |  | 
 | // An implementation of SingleThreadTaskRunner to be used in conjunction | 
 | // with BrowserThread. | 
 | // TODO(gab): Consider replacing this with |g_globals->task_runners| -- only | 
 | // works if none are requested before starting the threads. | 
 | class BrowserThreadTaskRunner : public base::SingleThreadTaskRunner { | 
 |  public: | 
 |   explicit BrowserThreadTaskRunner(BrowserThread::ID identifier) | 
 |       : id_(identifier) {} | 
 |  | 
 |   // SingleThreadTaskRunner implementation. | 
 |   bool PostDelayedTask(const base::Location& from_here, | 
 |                        base::OnceClosure task, | 
 |                        base::TimeDelta delay) override { | 
 |     return BrowserThread::PostDelayedTask(id_, from_here, std::move(task), | 
 |                                           delay); | 
 |   } | 
 |  | 
 |   bool PostNonNestableDelayedTask(const base::Location& from_here, | 
 |                                   base::OnceClosure task, | 
 |                                   base::TimeDelta delay) override { | 
 |     return BrowserThread::PostNonNestableDelayedTask(id_, from_here, | 
 |                                                      std::move(task), delay); | 
 |   } | 
 |  | 
 |   bool RunsTasksInCurrentSequence() const override { | 
 |     return BrowserThread::CurrentlyOn(id_); | 
 |   } | 
 |  | 
 |  protected: | 
 |   ~BrowserThreadTaskRunner() override {} | 
 |  | 
 |  private: | 
 |   BrowserThread::ID id_; | 
 |   DISALLOW_COPY_AND_ASSIGN(BrowserThreadTaskRunner); | 
 | }; | 
 |  | 
 | // A separate helper is used just for the task runners, in order to avoid | 
 | // needing to initialize the globals to create a task runner. | 
 | struct BrowserThreadTaskRunners { | 
 |   BrowserThreadTaskRunners() { | 
 |     for (int i = 0; i < BrowserThread::ID_COUNT; ++i) { | 
 |       proxies[i] = | 
 |           new BrowserThreadTaskRunner(static_cast<BrowserThread::ID>(i)); | 
 |     } | 
 |   } | 
 |  | 
 |   scoped_refptr<base::SingleThreadTaskRunner> proxies[BrowserThread::ID_COUNT]; | 
 | }; | 
 |  | 
 | base::LazyInstance<BrowserThreadTaskRunners>::Leaky g_task_runners = | 
 |     LAZY_INSTANCE_INITIALIZER; | 
 |  | 
 | // State of a given BrowserThread::ID in chronological order throughout the | 
 | // browser process' lifetime. | 
 | enum BrowserThreadState { | 
 |   // BrowserThread::ID isn't associated with anything yet. | 
 |   UNINITIALIZED = 0, | 
 |   // BrowserThread::ID is associated with a BrowserThreadImpl instance but the | 
 |   // underlying thread hasn't started yet. | 
 |   INITIALIZED, | 
 |   // BrowserThread::ID is associated to a TaskRunner and is accepting tasks. | 
 |   RUNNING, | 
 |   // BrowserThread::ID no longer accepts tasks. | 
 |   SHUTDOWN | 
 | }; | 
 |  | 
 | using BrowserThreadDelegateAtomicPtr = base::subtle::AtomicWord; | 
 |  | 
 | struct BrowserThreadGlobals { | 
 |   // This lock protects |task_runners| and |states|. Do not read or modify those | 
 |   // arrays without holding this lock. Do not block while holding this lock. | 
 |   base::Lock lock; | 
 |  | 
 |   // This array is filled either as the underlying threads start and invoke | 
 |   // Init() or in RedirectThreadIDToTaskRunner() for threads that are being | 
 |   // redirected. It is not emptied during shutdown in order to support | 
 |   // RunsTasksInCurrentSequence() until the very end. | 
 |   scoped_refptr<base::SingleThreadTaskRunner> | 
 |       task_runners[BrowserThread::ID_COUNT]; | 
 |  | 
 |   // Holds the state of each BrowserThread::ID. | 
 |   BrowserThreadState states[BrowserThread::ID_COUNT] = {}; | 
 |  | 
 |   // Only atomic operations are used on this pointer. The delegate isn't owned | 
 |   // by BrowserThreadGlobals, rather by whoever calls | 
 |   // BrowserThread::SetIOThreadDelegate. | 
 |   BrowserThreadDelegateAtomicPtr io_thread_delegate = 0; | 
 | }; | 
 |  | 
 | base::LazyInstance<BrowserThreadGlobals>::Leaky | 
 |     g_globals = LAZY_INSTANCE_INITIALIZER; | 
 |  | 
 | }  // namespace | 
 |  | 
 | BrowserThreadImpl::BrowserThreadImpl(ID identifier) | 
 |     : Thread(GetThreadName(identifier)), identifier_(identifier) { | 
 |   Initialize(); | 
 | } | 
 |  | 
 | BrowserThreadImpl::BrowserThreadImpl(ID identifier, | 
 |                                      base::MessageLoop* message_loop) | 
 |     : Thread(GetThreadName(identifier)), identifier_(identifier) { | 
 |   SetMessageLoop(message_loop); | 
 |   Initialize(); | 
 |  | 
 |   // If constructed with an explicit message loop, this is a fake | 
 |   // BrowserThread which runs on the current thread. | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |   base::AutoLock lock(globals.lock); | 
 |  | 
 |   DCHECK(!globals.task_runners[identifier_]); | 
 |   globals.task_runners[identifier_] = task_runner(); | 
 |  | 
 |   DCHECK_EQ(globals.states[identifier_], BrowserThreadState::INITIALIZED); | 
 |   globals.states[identifier_] = BrowserThreadState::RUNNING; | 
 | } | 
 |  | 
 | void BrowserThreadImpl::Init() { | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |  | 
 | #if DCHECK_IS_ON() | 
 |   { | 
 |     base::AutoLock lock(globals.lock); | 
 |     // |globals| should already have been initialized for |identifier_| in | 
 |     // BrowserThreadImpl::StartWithOptions(). If this isn't the case it's likely | 
 |     // because this BrowserThreadImpl's owner incorrectly used Thread::Start.*() | 
 |     // instead of BrowserThreadImpl::Start.*(). | 
 |     DCHECK_EQ(globals.states[identifier_], BrowserThreadState::RUNNING); | 
 |     DCHECK(globals.task_runners[identifier_]); | 
 |     DCHECK(globals.task_runners[identifier_]->RunsTasksInCurrentSequence()); | 
 |   } | 
 | #endif  // DCHECK_IS_ON() | 
 |  | 
 |   if (identifier_ == BrowserThread::DB || | 
 |       identifier_ == BrowserThread::FILE || | 
 |       identifier_ == BrowserThread::FILE_USER_BLOCKING || | 
 |       identifier_ == BrowserThread::PROCESS_LAUNCHER || | 
 |       identifier_ == BrowserThread::CACHE) { | 
 |     // Nesting and task observers are not allowed on redirected threads. | 
 |     base::RunLoop::DisallowNestingOnCurrentThread(); | 
 |     message_loop()->DisallowTaskObservers(); | 
 |   } | 
 |  | 
 |   if (identifier_ == BrowserThread::IO) { | 
 |     BrowserThreadDelegateAtomicPtr delegate = | 
 |         base::subtle::NoBarrier_Load(&globals.io_thread_delegate); | 
 |     if (delegate) | 
 |       reinterpret_cast<BrowserThreadDelegate*>(delegate)->Init(); | 
 |   } | 
 | } | 
 |  | 
 | // We disable optimizations for this block of functions so the compiler doesn't | 
 | // merge them all together. | 
 | MSVC_DISABLE_OPTIMIZE() | 
 | MSVC_PUSH_DISABLE_WARNING(4748) | 
 |  | 
 | NOINLINE void BrowserThreadImpl::UIThreadRun(base::RunLoop* run_loop) { | 
 |   volatile int line_number = __LINE__; | 
 |   Thread::Run(run_loop); | 
 |   CHECK_GT(line_number, 0); | 
 | } | 
 |  | 
 | NOINLINE void BrowserThreadImpl::DBThreadRun(base::RunLoop* run_loop) { | 
 |   volatile int line_number = __LINE__; | 
 |   Thread::Run(run_loop); | 
 |   CHECK_GT(line_number, 0); | 
 | } | 
 |  | 
 | NOINLINE void BrowserThreadImpl::FileThreadRun(base::RunLoop* run_loop) { | 
 |   volatile int line_number = __LINE__; | 
 |   Thread::Run(run_loop); | 
 |   CHECK_GT(line_number, 0); | 
 | } | 
 |  | 
 | NOINLINE void BrowserThreadImpl::FileUserBlockingThreadRun( | 
 |     base::RunLoop* run_loop) { | 
 |   volatile int line_number = __LINE__; | 
 |   Thread::Run(run_loop); | 
 |   CHECK_GT(line_number, 0); | 
 | } | 
 |  | 
 | NOINLINE void BrowserThreadImpl::ProcessLauncherThreadRun( | 
 |     base::RunLoop* run_loop) { | 
 |   volatile int line_number = __LINE__; | 
 |   Thread::Run(run_loop); | 
 |   CHECK_GT(line_number, 0); | 
 | } | 
 |  | 
 | NOINLINE void BrowserThreadImpl::CacheThreadRun(base::RunLoop* run_loop) { | 
 |   volatile int line_number = __LINE__; | 
 |   Thread::Run(run_loop); | 
 |   CHECK_GT(line_number, 0); | 
 | } | 
 |  | 
 | NOINLINE void BrowserThreadImpl::IOThreadRun(base::RunLoop* run_loop) { | 
 |   volatile int line_number = __LINE__; | 
 |   Thread::Run(run_loop); | 
 |   CHECK_GT(line_number, 0); | 
 | } | 
 |  | 
 | MSVC_POP_WARNING() | 
 | MSVC_ENABLE_OPTIMIZE(); | 
 |  | 
 | void BrowserThreadImpl::Run(base::RunLoop* run_loop) { | 
 | #if defined(OS_ANDROID) | 
 |   // Not to reset thread name to "Thread-???" by VM, attach VM with thread name. | 
 |   // Though it may create unnecessary VM thread objects, keeping thread name | 
 |   // gives more benefit in debugging in the platform. | 
 |   if (!thread_name().empty()) { | 
 |     base::android::AttachCurrentThreadWithName(thread_name()); | 
 |   } | 
 | #endif | 
 |  | 
 |   BrowserThread::ID thread_id = ID_COUNT; | 
 |   CHECK(GetCurrentThreadIdentifier(&thread_id)); | 
 |   CHECK_EQ(identifier_, thread_id); | 
 |  | 
 |   switch (identifier_) { | 
 |     case BrowserThread::UI: | 
 |       return UIThreadRun(run_loop); | 
 |     case BrowserThread::DB: | 
 |       return DBThreadRun(run_loop); | 
 |     case BrowserThread::FILE: | 
 |       return FileThreadRun(run_loop); | 
 |     case BrowserThread::FILE_USER_BLOCKING: | 
 |       return FileUserBlockingThreadRun(run_loop); | 
 |     case BrowserThread::PROCESS_LAUNCHER: | 
 |       return ProcessLauncherThreadRun(run_loop); | 
 |     case BrowserThread::CACHE: | 
 |       return CacheThreadRun(run_loop); | 
 |     case BrowserThread::IO: | 
 |       return IOThreadRun(run_loop); | 
 |     case BrowserThread::ID_COUNT: | 
 |       CHECK(false);  // This shouldn't actually be reached! | 
 |       break; | 
 |   } | 
 |  | 
 |   // |identifier_| must be set to a valid enum value in the constructor, so it | 
 |   // should be impossible to reach here. | 
 |   CHECK(false); | 
 | } | 
 |  | 
 | void BrowserThreadImpl::CleanUp() { | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |  | 
 |   if (identifier_ == BrowserThread::IO) { | 
 |     BrowserThreadDelegateAtomicPtr delegate = | 
 |         base::subtle::NoBarrier_Load(&globals.io_thread_delegate); | 
 |     if (delegate) | 
 |       reinterpret_cast<BrowserThreadDelegate*>(delegate)->CleanUp(); | 
 |   } | 
 |  | 
 |   // Change the state to SHUTDOWN so that PostTaskHelper stops accepting tasks | 
 |   // for this thread. Do not clear globals.task_runners[identifier_] so that | 
 |   // BrowserThread::CurrentlyOn() works from the MessageLoop's | 
 |   // DestructionObservers. | 
 |   base::AutoLock lock(globals.lock); | 
 |   DCHECK_EQ(globals.states[identifier_], BrowserThreadState::RUNNING); | 
 |   globals.states[identifier_] = BrowserThreadState::SHUTDOWN; | 
 | } | 
 |  | 
 | void BrowserThreadImpl::Initialize() { | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |  | 
 |   base::AutoLock lock(globals.lock); | 
 |   DCHECK_GE(identifier_, 0); | 
 |   DCHECK_LT(identifier_, ID_COUNT); | 
 |   DCHECK_EQ(globals.states[identifier_], BrowserThreadState::UNINITIALIZED); | 
 |   globals.states[identifier_] = BrowserThreadState::INITIALIZED; | 
 | } | 
 |  | 
 | // static | 
 | void BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::ID identifier) { | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |  | 
 |   base::AutoLock lock(globals.lock); | 
 |   DCHECK_EQ(globals.states[identifier], BrowserThreadState::SHUTDOWN); | 
 |   globals.states[identifier] = BrowserThreadState::UNINITIALIZED; | 
 |   globals.task_runners[identifier] = nullptr; | 
 |   if (identifier == BrowserThread::IO) | 
 |     SetIOThreadDelegate(nullptr); | 
 | } | 
 |  | 
 | BrowserThreadImpl::~BrowserThreadImpl() { | 
 |   // All Thread subclasses must call Stop() in the destructor. This is | 
 |   // doubly important here as various bits of code check they are on | 
 |   // the right BrowserThread. | 
 |   Stop(); | 
 |  | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |   base::AutoLock lock(globals.lock); | 
 |   // This thread should have gone through Cleanup() as part of Stop() and be in | 
 |   // the SHUTDOWN state already (unless it uses an externally provided | 
 |   // MessageLoop instead of a real underlying thread and thus doesn't go through | 
 |   // Cleanup()). | 
 |   if (using_external_message_loop()) { | 
 |     DCHECK_EQ(globals.states[identifier_], BrowserThreadState::RUNNING); | 
 |     globals.states[identifier_] = BrowserThreadState::SHUTDOWN; | 
 |   } else { | 
 |     DCHECK_EQ(globals.states[identifier_], BrowserThreadState::SHUTDOWN); | 
 |   } | 
 | #if DCHECK_IS_ON() | 
 |   // Double check that the threads are ordered correctly in the enumeration. | 
 |   for (int i = identifier_ + 1; i < ID_COUNT; ++i) { | 
 |     DCHECK(globals.states[i] == BrowserThreadState::SHUTDOWN || | 
 |            globals.states[i] == BrowserThreadState::UNINITIALIZED) | 
 |         << "Threads must be listed in the reverse order that they die"; | 
 |   } | 
 | #endif | 
 | } | 
 |  | 
 | bool BrowserThreadImpl::Start() { | 
 |   return StartWithOptions(base::Thread::Options()); | 
 | } | 
 |  | 
 | bool BrowserThreadImpl::StartWithOptions(const Options& options) { | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |  | 
 |   // Holding the lock is necessary when kicking off the thread to ensure | 
 |   // |states| and |task_runners| are updated before it gets to query them. | 
 |   base::AutoLock lock(globals.lock); | 
 |  | 
 |   bool result = Thread::StartWithOptions(options); | 
 |  | 
 |   // Although the thread is starting asynchronously, the MessageLoop is already | 
 |   // ready to accept tasks and as such this BrowserThreadImpl is considered as | 
 |   // "running". | 
 |   DCHECK(!globals.task_runners[identifier_]); | 
 |   globals.task_runners[identifier_] = task_runner(); | 
 |   DCHECK(globals.task_runners[identifier_]); | 
 |  | 
 |   DCHECK_EQ(globals.states[identifier_], BrowserThreadState::INITIALIZED); | 
 |   globals.states[identifier_] = BrowserThreadState::RUNNING; | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | bool BrowserThreadImpl::StartAndWaitForTesting() { | 
 |   if (!Start()) | 
 |     return false; | 
 |   WaitUntilThreadStarted(); | 
 |   return true; | 
 | } | 
 |  | 
 | // static | 
 | void BrowserThreadImpl::RedirectThreadIDToTaskRunner( | 
 |     BrowserThread::ID identifier, | 
 |     scoped_refptr<base::SingleThreadTaskRunner> task_runner) { | 
 |   DCHECK(task_runner); | 
 |  | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |   base::AutoLock lock(globals.lock); | 
 |  | 
 |   DCHECK(!globals.task_runners[identifier]); | 
 |   DCHECK_EQ(globals.states[identifier], BrowserThreadState::UNINITIALIZED); | 
 |  | 
 |   globals.task_runners[identifier] = std::move(task_runner); | 
 |   globals.states[identifier] = BrowserThreadState::RUNNING; | 
 | } | 
 |  | 
 | // static | 
 | void BrowserThreadImpl::StopRedirectionOfThreadID( | 
 |     BrowserThread::ID identifier) { | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |   base::AutoLock auto_lock(globals.lock); | 
 |  | 
 |   DCHECK(globals.task_runners[identifier]); | 
 |  | 
 |   // Change the state to SHUTDOWN to stop accepting new tasks. Note: this is | 
 |   // different from non-redirected threads which continue accepting tasks while | 
 |   // being joined and only quit when idle. However, any tasks for which this | 
 |   // difference matters was already racy as any thread posting a task after the | 
 |   // Signal task below can't be synchronized with the joining thread. Therefore, | 
 |   // that task could already come in before or after the join had completed in | 
 |   // the non-redirection world. Entering SHUTDOWN early merely skews this race | 
 |   // towards making it less likely such a task is accepted by the joined thread | 
 |   // which is fine. | 
 |   DCHECK_EQ(globals.states[identifier], BrowserThreadState::RUNNING); | 
 |   globals.states[identifier] = BrowserThreadState::SHUTDOWN; | 
 |  | 
 |   // Wait for all pending tasks to complete. | 
 |   base::WaitableEvent flushed(base::WaitableEvent::ResetPolicy::MANUAL, | 
 |                               base::WaitableEvent::InitialState::NOT_SIGNALED); | 
 |   globals.task_runners[identifier]->PostTask( | 
 |       FROM_HERE, | 
 |       base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&flushed))); | 
 |   { | 
 |     base::AutoUnlock auto_lock(globals.lock); | 
 |     flushed.Wait(); | 
 |   } | 
 |  | 
 |   // Only reset the task runner after running pending tasks so that | 
 |   // BrowserThread::CurrentlyOn() works in their scope. | 
 |   globals.task_runners[identifier] = nullptr; | 
 |  | 
 |   // Note: it's still possible for tasks to be posted to that task runner after | 
 |   // this point (e.g. through a previously obtained ThreadTaskRunnerHandle or by | 
 |   // one of the last tasks re-posting to its ThreadTaskRunnerHandle) but the | 
 |   // BrowserThread API itself won't accept tasks. Such tasks are ultimately | 
 |   // guaranteed to run before TaskScheduler::Shutdown() returns but may break | 
 |   // the assumption in PostTaskHelper that BrowserThread::ID A > B will always | 
 |   // succeed to post to B. This is pretty much the only observable difference | 
 |   // between a redirected thread and a real one and is one we're willing to live | 
 |   // with for this experiment. TODO(gab): fix this before enabling the | 
 |   // experiment by default on trunk, http://crbug.com/653916. | 
 | } | 
 |  | 
 | // static | 
 | bool BrowserThreadImpl::PostTaskHelper(BrowserThread::ID identifier, | 
 |                                        const base::Location& from_here, | 
 |                                        base::OnceClosure task, | 
 |                                        base::TimeDelta delay, | 
 |                                        bool nestable) { | 
 |   DCHECK_GE(identifier, 0); | 
 |   DCHECK_LT(identifier, ID_COUNT); | 
 |   // Optimization: to avoid unnecessary locks, we listed the ID enumeration in | 
 |   // order of lifetime.  So no need to lock if we know that the target thread | 
 |   // outlives current thread as that implies the current thread only ever sees | 
 |   // the target thread in its RUNNING state. | 
 |   // Note: since the array is so small, ok to loop instead of creating a map, | 
 |   // which would require a lock because std::map isn't thread safe, defeating | 
 |   // the whole purpose of this optimization. | 
 |   BrowserThread::ID current_thread = ID_COUNT; | 
 |   bool target_thread_outlives_current = | 
 |       GetCurrentThreadIdentifier(¤t_thread) && | 
 |       current_thread >= identifier; | 
 |  | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |   if (!target_thread_outlives_current) | 
 |     globals.lock.Acquire(); | 
 |  | 
 |   const bool accepting_tasks = | 
 |       globals.states[identifier] == BrowserThreadState::RUNNING; | 
 |   if (accepting_tasks) { | 
 |     base::SingleThreadTaskRunner* task_runner = | 
 |         globals.task_runners[identifier].get(); | 
 |     DCHECK(task_runner); | 
 |     if (nestable) { | 
 |       task_runner->PostDelayedTask(from_here, std::move(task), delay); | 
 |     } else { | 
 |       task_runner->PostNonNestableDelayedTask(from_here, std::move(task), | 
 |                                               delay); | 
 |     } | 
 |   } | 
 |  | 
 |   if (!target_thread_outlives_current) | 
 |     globals.lock.Release(); | 
 |  | 
 |   return accepting_tasks; | 
 | } | 
 |  | 
 | // static | 
 | void BrowserThread::PostAfterStartupTask( | 
 |     const base::Location& from_here, | 
 |     const scoped_refptr<base::TaskRunner>& task_runner, | 
 |     base::OnceClosure task) { | 
 |   GetContentClient()->browser()->PostAfterStartupTask(from_here, task_runner, | 
 |                                                       std::move(task)); | 
 | } | 
 |  | 
 | // static | 
 | bool BrowserThread::IsThreadInitialized(ID identifier) { | 
 |   if (!g_globals.IsCreated()) | 
 |     return false; | 
 |  | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |   base::AutoLock lock(globals.lock); | 
 |   DCHECK_GE(identifier, 0); | 
 |   DCHECK_LT(identifier, ID_COUNT); | 
 |   return globals.states[identifier] == BrowserThreadState::INITIALIZED || | 
 |          globals.states[identifier] == BrowserThreadState::RUNNING; | 
 | } | 
 |  | 
 | // static | 
 | bool BrowserThread::CurrentlyOn(ID identifier) { | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |   base::AutoLock lock(globals.lock); | 
 |   DCHECK_GE(identifier, 0); | 
 |   DCHECK_LT(identifier, ID_COUNT); | 
 |   return globals.task_runners[identifier] && | 
 |          globals.task_runners[identifier]->RunsTasksInCurrentSequence(); | 
 | } | 
 |  | 
 | // static | 
 | std::string BrowserThread::GetDCheckCurrentlyOnErrorMessage(ID expected) { | 
 |   std::string actual_name = base::PlatformThread::GetName(); | 
 |   if (actual_name.empty()) | 
 |     actual_name = "Unknown Thread"; | 
 |  | 
 |   std::string result = "Must be called on "; | 
 |   result += GetThreadName(expected); | 
 |   result += "; actually called on "; | 
 |   result += actual_name; | 
 |   result += "."; | 
 |   return result; | 
 | } | 
 |  | 
 | // static | 
 | bool BrowserThread::IsMessageLoopValid(ID identifier) { | 
 |   if (!g_globals.IsCreated()) | 
 |     return false; | 
 |  | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |   base::AutoLock lock(globals.lock); | 
 |   DCHECK_GE(identifier, 0); | 
 |   DCHECK_LT(identifier, ID_COUNT); | 
 |   return globals.states[identifier] == BrowserThreadState::RUNNING; | 
 | } | 
 |  | 
 | // static | 
 | bool BrowserThread::PostTask(ID identifier, | 
 |                              const base::Location& from_here, | 
 |                              base::OnceClosure task) { | 
 |   return BrowserThreadImpl::PostTaskHelper( | 
 |       identifier, from_here, std::move(task), base::TimeDelta(), true); | 
 | } | 
 |  | 
 | // static | 
 | bool BrowserThread::PostDelayedTask(ID identifier, | 
 |                                     const base::Location& from_here, | 
 |                                     base::OnceClosure task, | 
 |                                     base::TimeDelta delay) { | 
 |   return BrowserThreadImpl::PostTaskHelper(identifier, from_here, | 
 |                                            std::move(task), delay, true); | 
 | } | 
 |  | 
 | // static | 
 | bool BrowserThread::PostNonNestableTask(ID identifier, | 
 |                                         const base::Location& from_here, | 
 |                                         base::OnceClosure task) { | 
 |   return BrowserThreadImpl::PostTaskHelper( | 
 |       identifier, from_here, std::move(task), base::TimeDelta(), false); | 
 | } | 
 |  | 
 | // static | 
 | bool BrowserThread::PostNonNestableDelayedTask(ID identifier, | 
 |                                                const base::Location& from_here, | 
 |                                                base::OnceClosure task, | 
 |                                                base::TimeDelta delay) { | 
 |   return BrowserThreadImpl::PostTaskHelper(identifier, from_here, | 
 |                                            std::move(task), delay, false); | 
 | } | 
 |  | 
 | // static | 
 | bool BrowserThread::PostTaskAndReply(ID identifier, | 
 |                                      const base::Location& from_here, | 
 |                                      base::OnceClosure task, | 
 |                                      base::OnceClosure reply) { | 
 |   return GetTaskRunnerForThread(identifier) | 
 |       ->PostTaskAndReply(from_here, std::move(task), std::move(reply)); | 
 | } | 
 |  | 
 | // static | 
 | bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) { | 
 |   if (!g_globals.IsCreated()) | 
 |     return false; | 
 |  | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |   // Profiler to track potential contention on |globals.lock|. This only does | 
 |   // real work on canary and local dev builds, so the cost of having this here | 
 |   // should be minimal. | 
 |   base::AutoLock lock(globals.lock); | 
 |   for (int i = 0; i < ID_COUNT; ++i) { | 
 |     if (globals.task_runners[i] && | 
 |         globals.task_runners[i]->RunsTasksInCurrentSequence()) { | 
 |       *identifier = static_cast<ID>(i); | 
 |       return true; | 
 |     } | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | // static | 
 | scoped_refptr<base::SingleThreadTaskRunner> | 
 | BrowserThread::GetTaskRunnerForThread(ID identifier) { | 
 |   return g_task_runners.Get().proxies[identifier]; | 
 | } | 
 |  | 
 | // static | 
 | void BrowserThread::SetIOThreadDelegate(BrowserThreadDelegate* delegate) { | 
 |   BrowserThreadGlobals& globals = g_globals.Get(); | 
 |   BrowserThreadDelegateAtomicPtr old_delegate = | 
 |       base::subtle::NoBarrier_AtomicExchange( | 
 |           &globals.io_thread_delegate, | 
 |           reinterpret_cast<BrowserThreadDelegateAtomicPtr>(delegate)); | 
 |  | 
 |   // This catches registration when previously registered. | 
 |   DCHECK(!delegate || !old_delegate); | 
 | } | 
 |  | 
 | }  // namespace content |