diff options
author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/content/browser/browser_thread_impl.cc | |
download | qtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz |
Initial import.
Diffstat (limited to 'chromium/content/browser/browser_thread_impl.cc')
-rw-r--r-- | chromium/content/browser/browser_thread_impl.cc | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/chromium/content/browser/browser_thread_impl.cc b/chromium/content/browser/browser_thread_impl.cc new file mode 100644 index 00000000000..fb6feac452f --- /dev/null +++ b/chromium/content/browser/browser_thread_impl.cc @@ -0,0 +1,482 @@ +// 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 "base/atomicops.h" +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/lazy_instance.h" +#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/threading/thread_restrictions.h" +#include "content/public/browser/browser_thread_delegate.h" + +namespace content { + +namespace { + +// Friendly names for the well-known threads. +static const char* 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 +}; + +struct BrowserThreadGlobals { + BrowserThreadGlobals() + : blocking_pool(new base::SequencedWorkerPool(3, "BrowserBlocking")) { + memset(threads, 0, BrowserThread::ID_COUNT * sizeof(threads[0])); + memset(thread_delegates, 0, + BrowserThread::ID_COUNT * sizeof(thread_delegates[0])); + } + + // This lock protects |threads|. Do not read or modify that array + // without holding this lock. Do not block while holding this lock. + base::Lock lock; + + // This array is protected by |lock|. The threads are not owned by this + // array. Typically, the threads are owned on the UI thread by + // BrowserMainLoop. BrowserThreadImpl objects remove themselves from this + // array upon destruction. + BrowserThreadImpl* threads[BrowserThread::ID_COUNT]; + + // Only atomic operations are used on this array. The delegates are not owned + // by this array, rather by whoever calls BrowserThread::SetDelegate. + BrowserThreadDelegate* thread_delegates[BrowserThread::ID_COUNT]; + + const scoped_refptr<base::SequencedWorkerPool> blocking_pool; +}; + +base::LazyInstance<BrowserThreadGlobals>::Leaky + g_globals = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +BrowserThreadImpl::BrowserThreadImpl(ID identifier) + : Thread(g_browser_thread_names[identifier]), + identifier_(identifier) { + Initialize(); +} + +BrowserThreadImpl::BrowserThreadImpl(ID identifier, + base::MessageLoop* message_loop) + : Thread(message_loop->thread_name().c_str()), identifier_(identifier) { + set_message_loop(message_loop); + Initialize(); +} + +// static +void BrowserThreadImpl::ShutdownThreadPool() { + // The goal is to make it impossible for chrome to 'infinite loop' during + // shutdown, but to reasonably expect that all BLOCKING_SHUTDOWN tasks queued + // during shutdown get run. There's nothing particularly scientific about the + // number chosen. + const int kMaxNewShutdownBlockingTasks = 1000; + BrowserThreadGlobals& globals = g_globals.Get(); + globals.blocking_pool->Shutdown(kMaxNewShutdownBlockingTasks); +} + +// static +void BrowserThreadImpl::FlushThreadPoolHelper() { + // We don't want to create a pool if none exists. + if (g_globals == NULL) + return; + g_globals.Get().blocking_pool->FlushForTesting(); +} + +void BrowserThreadImpl::Init() { + BrowserThreadGlobals& globals = g_globals.Get(); + + using base::subtle::AtomicWord; + AtomicWord* storage = + reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]); + AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage); + BrowserThreadDelegate* delegate = + reinterpret_cast<BrowserThreadDelegate*>(stored_pointer); + if (delegate) { + delegate->Init(); + message_loop()->PostTask(FROM_HERE, + base::Bind(&BrowserThreadDelegate::InitAsync, + // Delegate is expected to exist for the + // duration of the thread's lifetime + base::Unretained(delegate))); + } +} + +// 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::MessageLoop* message_loop) { + volatile int line_number = __LINE__; + Thread::Run(message_loop); + CHECK_GT(line_number, 0); +} + +NOINLINE void BrowserThreadImpl::DBThreadRun(base::MessageLoop* message_loop) { + volatile int line_number = __LINE__; + Thread::Run(message_loop); + CHECK_GT(line_number, 0); +} + +NOINLINE void BrowserThreadImpl::FileThreadRun( + base::MessageLoop* message_loop) { + volatile int line_number = __LINE__; + Thread::Run(message_loop); + CHECK_GT(line_number, 0); +} + +NOINLINE void BrowserThreadImpl::FileUserBlockingThreadRun( + base::MessageLoop* message_loop) { + volatile int line_number = __LINE__; + Thread::Run(message_loop); + CHECK_GT(line_number, 0); +} + +NOINLINE void BrowserThreadImpl::ProcessLauncherThreadRun( + base::MessageLoop* message_loop) { + volatile int line_number = __LINE__; + Thread::Run(message_loop); + CHECK_GT(line_number, 0); +} + +NOINLINE void BrowserThreadImpl::CacheThreadRun( + base::MessageLoop* message_loop) { + volatile int line_number = __LINE__; + Thread::Run(message_loop); + CHECK_GT(line_number, 0); +} + +NOINLINE void BrowserThreadImpl::IOThreadRun(base::MessageLoop* message_loop) { + volatile int line_number = __LINE__; + Thread::Run(message_loop); + CHECK_GT(line_number, 0); +} + +MSVC_POP_WARNING() +MSVC_ENABLE_OPTIMIZE(); + +void BrowserThreadImpl::Run(base::MessageLoop* message_loop) { + BrowserThread::ID thread_id; + if (!GetCurrentThreadIdentifier(&thread_id)) + return Thread::Run(message_loop); + + switch (thread_id) { + case BrowserThread::UI: + return UIThreadRun(message_loop); + case BrowserThread::DB: + return DBThreadRun(message_loop); + case BrowserThread::FILE: + return FileThreadRun(message_loop); + case BrowserThread::FILE_USER_BLOCKING: + return FileUserBlockingThreadRun(message_loop); + case BrowserThread::PROCESS_LAUNCHER: + return ProcessLauncherThreadRun(message_loop); + case BrowserThread::CACHE: + return CacheThreadRun(message_loop); + case BrowserThread::IO: + return IOThreadRun(message_loop); + case BrowserThread::ID_COUNT: + CHECK(false); // This shouldn't actually be reached! + break; + } + Thread::Run(message_loop); +} + +void BrowserThreadImpl::CleanUp() { + BrowserThreadGlobals& globals = g_globals.Get(); + + using base::subtle::AtomicWord; + AtomicWord* storage = + reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]); + AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage); + BrowserThreadDelegate* delegate = + reinterpret_cast<BrowserThreadDelegate*>(stored_pointer); + + if (delegate) + delegate->CleanUp(); +} + +void BrowserThreadImpl::Initialize() { + BrowserThreadGlobals& globals = g_globals.Get(); + + base::AutoLock lock(globals.lock); + DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT); + DCHECK(globals.threads[identifier_] == NULL); + globals.threads[identifier_] = this; +} + +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); + globals.threads[identifier_] = NULL; +#ifndef NDEBUG + // Double check that the threads are ordered correctly in the enumeration. + for (int i = identifier_ + 1; i < ID_COUNT; ++i) { + DCHECK(!globals.threads[i]) << + "Threads must be listed in the reverse order that they die"; + } +#endif +} + +// static +bool BrowserThreadImpl::PostTaskHelper( + BrowserThread::ID identifier, + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay, + bool nestable) { + DCHECK(identifier >= 0 && 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. + // 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; + bool target_thread_outlives_current = + GetCurrentThreadIdentifier(¤t_thread) && + current_thread >= identifier; + + BrowserThreadGlobals& globals = g_globals.Get(); + if (!target_thread_outlives_current) + globals.lock.Acquire(); + + base::MessageLoop* message_loop = + globals.threads[identifier] ? globals.threads[identifier]->message_loop() + : NULL; + if (message_loop) { + if (nestable) { + message_loop->PostDelayedTask(from_here, task, delay); + } else { + message_loop->PostNonNestableDelayedTask(from_here, task, delay); + } + } + + if (!target_thread_outlives_current) + globals.lock.Release(); + + return !!message_loop; +} + +// An implementation of MessageLoopProxy to be used in conjunction +// with BrowserThread. +class BrowserThreadMessageLoopProxy : public base::MessageLoopProxy { + public: + explicit BrowserThreadMessageLoopProxy(BrowserThread::ID identifier) + : id_(identifier) { + } + + // MessageLoopProxy implementation. + virtual bool PostDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, base::TimeDelta delay) OVERRIDE { + return BrowserThread::PostDelayedTask(id_, from_here, task, delay); + } + + virtual bool PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) OVERRIDE { + return BrowserThread::PostNonNestableDelayedTask(id_, from_here, task, + delay); + } + + virtual bool RunsTasksOnCurrentThread() const OVERRIDE { + return BrowserThread::CurrentlyOn(id_); + } + + protected: + virtual ~BrowserThreadMessageLoopProxy() {} + + private: + BrowserThread::ID id_; + DISALLOW_COPY_AND_ASSIGN(BrowserThreadMessageLoopProxy); +}; + +// static +bool BrowserThread::PostBlockingPoolTask( + const tracked_objects::Location& from_here, + const base::Closure& task) { + return g_globals.Get().blocking_pool->PostWorkerTask(from_here, task); +} + +bool BrowserThread::PostBlockingPoolTaskAndReply( + const tracked_objects::Location& from_here, + const base::Closure& task, + const base::Closure& reply) { + return g_globals.Get().blocking_pool->PostTaskAndReply( + from_here, task, reply); +} + +// static +bool BrowserThread::PostBlockingPoolSequencedTask( + const std::string& sequence_token_name, + const tracked_objects::Location& from_here, + const base::Closure& task) { + return g_globals.Get().blocking_pool->PostNamedSequencedWorkerTask( + sequence_token_name, from_here, task); +} + +// static +base::SequencedWorkerPool* BrowserThread::GetBlockingPool() { + return g_globals.Get().blocking_pool.get(); +} + +// static +bool BrowserThread::IsThreadInitialized(ID identifier) { + if (g_globals == NULL) + return false; + + BrowserThreadGlobals& globals = g_globals.Get(); + base::AutoLock lock(globals.lock); + DCHECK(identifier >= 0 && identifier < ID_COUNT); + return globals.threads[identifier] != NULL; +} + +// static +bool BrowserThread::CurrentlyOn(ID identifier) { + // We shouldn't use MessageLoop::current() since it uses LazyInstance which + // may be deleted by ~AtExitManager when a WorkerPool thread calls this + // function. + // http://crbug.com/63678 + base::ThreadRestrictions::ScopedAllowSingleton allow_singleton; + BrowserThreadGlobals& globals = g_globals.Get(); + base::AutoLock lock(globals.lock); + DCHECK(identifier >= 0 && identifier < ID_COUNT); + return globals.threads[identifier] && + globals.threads[identifier]->message_loop() == + base::MessageLoop::current(); +} + +// static +bool BrowserThread::IsMessageLoopValid(ID identifier) { + if (g_globals == NULL) + return false; + + BrowserThreadGlobals& globals = g_globals.Get(); + base::AutoLock lock(globals.lock); + DCHECK(identifier >= 0 && identifier < ID_COUNT); + return globals.threads[identifier] && + globals.threads[identifier]->message_loop(); +} + +// static +bool BrowserThread::PostTask(ID identifier, + const tracked_objects::Location& from_here, + const base::Closure& task) { + return BrowserThreadImpl::PostTaskHelper( + identifier, from_here, task, base::TimeDelta(), true); +} + +// static +bool BrowserThread::PostDelayedTask(ID identifier, + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) { + return BrowserThreadImpl::PostTaskHelper( + identifier, from_here, task, delay, true); +} + +// static +bool BrowserThread::PostNonNestableTask( + ID identifier, + const tracked_objects::Location& from_here, + const base::Closure& task) { + return BrowserThreadImpl::PostTaskHelper( + identifier, from_here, task, base::TimeDelta(), false); +} + +// static +bool BrowserThread::PostNonNestableDelayedTask( + ID identifier, + const tracked_objects::Location& from_here, + const base::Closure& task, + base::TimeDelta delay) { + return BrowserThreadImpl::PostTaskHelper( + identifier, from_here, task, delay, false); +} + +// static +bool BrowserThread::PostTaskAndReply( + ID identifier, + const tracked_objects::Location& from_here, + const base::Closure& task, + const base::Closure& reply) { + return GetMessageLoopProxyForThread(identifier)->PostTaskAndReply(from_here, + task, + reply); +} + +// static +bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) { + if (g_globals == NULL) + return false; + + // We shouldn't use MessageLoop::current() since it uses LazyInstance which + // may be deleted by ~AtExitManager when a WorkerPool thread calls this + // function. + // http://crbug.com/63678 + base::ThreadRestrictions::ScopedAllowSingleton allow_singleton; + base::MessageLoop* cur_message_loop = base::MessageLoop::current(); + BrowserThreadGlobals& globals = g_globals.Get(); + for (int i = 0; i < ID_COUNT; ++i) { + if (globals.threads[i] && + globals.threads[i]->message_loop() == cur_message_loop) { + *identifier = globals.threads[i]->identifier_; + return true; + } + } + + return false; +} + +// static +scoped_refptr<base::MessageLoopProxy> +BrowserThread::GetMessageLoopProxyForThread(ID identifier) { + return make_scoped_refptr(new BrowserThreadMessageLoopProxy(identifier)); +} + +// static +base::MessageLoop* BrowserThread::UnsafeGetMessageLoopForThread(ID identifier) { + if (g_globals == NULL) + return NULL; + + BrowserThreadGlobals& globals = g_globals.Get(); + base::AutoLock lock(globals.lock); + base::Thread* thread = globals.threads[identifier]; + DCHECK(thread); + base::MessageLoop* loop = thread->message_loop(); + return loop; +} + +// static +void BrowserThread::SetDelegate(ID identifier, + BrowserThreadDelegate* delegate) { + using base::subtle::AtomicWord; + BrowserThreadGlobals& globals = g_globals.Get(); + AtomicWord* storage = reinterpret_cast<AtomicWord*>( + &globals.thread_delegates[identifier]); + AtomicWord old_pointer = base::subtle::NoBarrier_AtomicExchange( + storage, reinterpret_cast<AtomicWord>(delegate)); + + // This catches registration when previously registered. + DCHECK(!delegate || !old_pointer); +} + +} // namespace content |