summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-19 04:11:57 +0000
committerbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-19 04:11:57 +0000
commit3189013eebc55ea5493186b29f2cee2bc7c0dafc (patch)
treef9c867f92c5fd9325a7d7e6ea9fed638103c4f4b
parent1946e0c4351fbdfd5b012d3c2aeac2c9218ad027 (diff)
downloadchromium_src-3189013eebc55ea5493186b29f2cee2bc7c0dafc.zip
chromium_src-3189013eebc55ea5493186b29f2cee2bc7c0dafc.tar.gz
chromium_src-3189013eebc55ea5493186b29f2cee2bc7c0dafc.tar.bz2
Hook up the SequencedWorkerPool to the browser thread.
[re-land of 116816 http://codereview.chromium.org/9065009] This does some refactoring of the static data in the browser thread so we only have one global object instead of a bunch fo separate arrays. It also hooks up the visited link master's I/O to use this new system as a proof of concept. Review URL: https://chromiumcodereview.appspot.com/9124033 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@118236 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/threading/sequenced_worker_pool.cc79
-rw-r--r--base/threading/sequenced_worker_pool.h16
-rw-r--r--chrome/browser/crash_upload_list.cc4
-rw-r--r--chrome/browser/visitedlink/visitedlink_master.cc22
-rw-r--r--chrome/browser/visitedlink/visitedlink_master.h8
-rw-r--r--chrome/browser/visitedlink/visitedlink_unittest.cc3
-rw-r--r--content/browser/browser_main_loop.cc8
-rw-r--r--content/browser/browser_thread_impl.cc146
-rw-r--r--content/browser/browser_thread_impl.h2
-rw-r--r--content/public/browser/browser_thread.h29
10 files changed, 241 insertions, 76 deletions
diff --git a/base/threading/sequenced_worker_pool.cc b/base/threading/sequenced_worker_pool.cc
index 05ccff1..6d47068 100644
--- a/base/threading/sequenced_worker_pool.cc
+++ b/base/threading/sequenced_worker_pool.cc
@@ -57,20 +57,33 @@ class SequencedWorkerPool::Inner
Inner(size_t max_threads, const std::string& thread_name_prefix);
virtual ~Inner();
- // Backends for SequenceWorkerPool.
SequenceToken GetSequenceToken();
+
SequenceToken GetNamedSequenceToken(const std::string& name);
- bool PostTask(int sequence_token_id,
+
+ // This function accepts a name and an ID. If the name is null, the
+ // token ID is used. This allows us to implement the optional name lookup
+ // from a single function without having to enter the lock a separate time.
+ bool PostTask(const std::string* optional_token_name,
+ int sequence_token_id,
SequencedWorkerPool::WorkerShutdown shutdown_behavior,
const tracked_objects::Location& from_here,
const base::Closure& task);
+
+ void Flush();
+
void Shutdown();
+
void SetTestingObserver(SequencedWorkerPool::TestingObserver* observer);
// Runs the worker loop on the background thread.
void ThreadLoop(Worker* this_worker);
private:
+ // Called from within the lock, this converts the given token name into a
+ // token ID, creating a new one if necessary.
+ int LockedGetNamedTokenID(const std::string& name);
+
// The calling code should clear the given delete_these_oustide_lock
// vector the next time the lock is released. See the implementation for
// a more detailed description.
@@ -235,18 +248,11 @@ SequencedWorkerPool::SequenceToken
SequencedWorkerPool::Inner::GetNamedSequenceToken(
const std::string& name) {
base::AutoLock lock(lock_);
- std::map<std::string, int>::const_iterator found =
- named_sequence_tokens_.find(name);
- if (found != named_sequence_tokens_.end())
- return SequenceToken(found->second); // Got an existing one.
-
- // Create a new one for this name.
- SequenceToken result = GetSequenceToken();
- named_sequence_tokens_.insert(std::make_pair(name, result.id_));
- return result;
+ return SequenceToken(LockedGetNamedTokenID(name));
}
bool SequencedWorkerPool::Inner::PostTask(
+ const std::string* optional_token_name,
int sequence_token_id,
SequencedWorkerPool::WorkerShutdown shutdown_behavior,
const tracked_objects::Location& from_here,
@@ -263,6 +269,10 @@ bool SequencedWorkerPool::Inner::PostTask(
if (terminating_)
return false;
+ // Now that we have the lock, apply the named token rules.
+ if (optional_token_name)
+ sequenced.sequence_token_id = LockedGetNamedTokenID(*optional_token_name);
+
pending_tasks_.push_back(sequenced);
pending_task_count_++;
if (shutdown_behavior == BLOCK_SHUTDOWN)
@@ -281,6 +291,15 @@ bool SequencedWorkerPool::Inner::PostTask(
return true;
}
+void SequencedWorkerPool::Inner::Flush() {
+ {
+ base::AutoLock lock(lock_);
+ while (pending_task_count_ > 0 || waiting_thread_count_ < threads_.size())
+ cond_var_.Wait();
+ }
+ cond_var_.Signal();
+}
+
void SequencedWorkerPool::Inner::Shutdown() {
if (shutdown_called_)
return;
@@ -366,6 +385,8 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
if (terminating_)
break;
waiting_thread_count_++;
+ cond_var_.Signal(); // For Flush() that may be waiting on the
+ // waiting thread count to go up.
cond_var_.Wait();
waiting_thread_count_--;
}
@@ -377,6 +398,22 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
cond_var_.Signal();
}
+int SequencedWorkerPool::Inner::LockedGetNamedTokenID(
+ const std::string& name) {
+ lock_.AssertAcquired();
+ DCHECK(!name.empty());
+
+ std::map<std::string, int>::const_iterator found =
+ named_sequence_tokens_.find(name);
+ if (found != named_sequence_tokens_.end())
+ return found->second; // Got an existing one.
+
+ // Create a new one for this name.
+ SequenceToken result = GetSequenceToken();
+ named_sequence_tokens_.insert(std::make_pair(name, result.id_));
+ return result.id_;
+}
+
bool SequencedWorkerPool::Inner::GetWork(
SequencedTask* task,
std::vector<base::Closure>* delete_these_outside_lock) {
@@ -593,33 +630,45 @@ SequencedWorkerPool::SequenceToken SequencedWorkerPool::GetNamedSequenceToken(
bool SequencedWorkerPool::PostWorkerTask(
const tracked_objects::Location& from_here,
const base::Closure& task) {
- return inner_->PostTask(0, BLOCK_SHUTDOWN, from_here, task);
+ return inner_->PostTask(NULL, 0, BLOCK_SHUTDOWN, from_here, task);
}
bool SequencedWorkerPool::PostWorkerTaskWithShutdownBehavior(
const tracked_objects::Location& from_here,
const base::Closure& task,
WorkerShutdown shutdown_behavior) {
- return inner_->PostTask(0, shutdown_behavior, from_here, task);
+ return inner_->PostTask(NULL, 0, shutdown_behavior, from_here, task);
}
bool SequencedWorkerPool::PostSequencedWorkerTask(
SequenceToken sequence_token,
const tracked_objects::Location& from_here,
const base::Closure& task) {
- return inner_->PostTask(sequence_token.id_, BLOCK_SHUTDOWN,
+ return inner_->PostTask(NULL, sequence_token.id_, BLOCK_SHUTDOWN,
from_here, task);
}
+bool SequencedWorkerPool::PostNamedSequencedWorkerTask(
+ const std::string& token_name,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ DCHECK(!token_name.empty());
+ return inner_->PostTask(&token_name, 0, BLOCK_SHUTDOWN, from_here, task);
+}
+
bool SequencedWorkerPool::PostSequencedWorkerTaskWithShutdownBehavior(
SequenceToken sequence_token,
const tracked_objects::Location& from_here,
const base::Closure& task,
WorkerShutdown shutdown_behavior) {
- return inner_->PostTask(sequence_token.id_, shutdown_behavior,
+ return inner_->PostTask(NULL, sequence_token.id_, shutdown_behavior,
from_here, task);
}
+void SequencedWorkerPool::FlushForTesting() {
+ inner_->Flush();
+}
+
void SequencedWorkerPool::Shutdown() {
inner_->Shutdown();
}
diff --git a/base/threading/sequenced_worker_pool.h b/base/threading/sequenced_worker_pool.h
index c6e0560..f5770a7 100644
--- a/base/threading/sequenced_worker_pool.h
+++ b/base/threading/sequenced_worker_pool.h
@@ -172,6 +172,12 @@ class BASE_EXPORT SequencedWorkerPool {
const tracked_objects::Location& from_here,
const base::Closure& task);
+ // Like PostSequencedWorkerTask above, but allows you to specify a named
+ // token, which saves an extra call to GetNamedSequenceToken.
+ bool PostNamedSequencedWorkerTask(const std::string& token_name,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
// Same as PostSequencedWorkerTask but allows specification of the shutdown
// behavior.
bool PostSequencedWorkerTaskWithShutdownBehavior(
@@ -180,6 +186,16 @@ class BASE_EXPORT SequencedWorkerPool {
const base::Closure& task,
WorkerShutdown shutdown_behavior);
+ // Blocks until all pending tasks are complete. This should only be called in
+ // unit tests when you want to validate something that should have happened.
+ //
+ // Note that calling this will not prevent other threads from posting work to
+ // the queue while the calling thread is waiting on Flush(). In this case,
+ // Flush will return only when there's no more work in the queue. Normally,
+ // this doesn't come up sine in a test, all the work is being posted from
+ // the main thread.
+ void FlushForTesting();
+
// Implements the worker pool shutdown. This should be called during app
// shutdown, and will discard/join with appropriate tasks before returning.
// After this call, subsequent calls to post tasks will fail.
diff --git a/chrome/browser/crash_upload_list.cc b/chrome/browser/crash_upload_list.cc
index bc74d8e..30c04f8 100644
--- a/chrome/browser/crash_upload_list.cc
+++ b/chrome/browser/crash_upload_list.cc
@@ -39,8 +39,7 @@ CrashUploadList::CrashUploadList(Delegate* delegate) : delegate_(delegate) {}
CrashUploadList::~CrashUploadList() {}
void CrashUploadList::LoadCrashListAsynchronously() {
- BrowserThread::PostTask(
- BrowserThread::FILE,
+ BrowserThread::PostBlockingPoolTask(
FROM_HERE,
base::Bind(&CrashUploadList::LoadCrashListAndInformDelegateOfCompletion,
this));
@@ -60,7 +59,6 @@ void CrashUploadList::LoadCrashListAndInformDelegateOfCompletion() {
}
void CrashUploadList::LoadCrashList() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
FilePath crash_dir_path;
PathService::Get(chrome::DIR_CRASH_DUMPS, &crash_dir_path);
FilePath upload_log_path = crash_dir_path.AppendASCII("uploads.log");
diff --git a/chrome/browser/visitedlink/visitedlink_master.cc b/chrome/browser/visitedlink/visitedlink_master.cc
index f3fbc85..38c87ca 100644
--- a/chrome/browser/visitedlink/visitedlink_master.cc
+++ b/chrome/browser/visitedlink/visitedlink_master.cc
@@ -191,6 +191,7 @@ void VisitedLinkMaster::InitMembers(Listener* listener, Profile* profile) {
history_service_override_ = NULL;
suppress_rebuild_ = false;
profile_ = profile;
+ sequence_token_ = BrowserThread::GetBlockingPool()->GetSequenceToken();
#ifndef NDEBUG
posted_asynchronous_operation_ = false;
@@ -242,6 +243,12 @@ VisitedLinkMaster::Hash VisitedLinkMaster::TryToAddURL(const GURL& url) {
return AddFingerprint(fingerprint, true);
}
+void VisitedLinkMaster::PostIOTask(const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ BrowserThread::GetBlockingPool()->PostSequencedWorkerTask(sequence_token_,
+ from_here, task);
+}
+
void VisitedLinkMaster::AddURL(const GURL& url) {
Hash index = TryToAddURL(url);
if (!table_builder_ && index != null_hash_) {
@@ -480,10 +487,7 @@ bool VisitedLinkMaster::WriteFullTable() {
hash_table_, table_length_ * sizeof(Fingerprint));
// The hash table may have shrunk, so make sure this is the end.
- BrowserThread::PostTask(
- BrowserThread::FILE,
- FROM_HERE,
- base::Bind(base::IgnoreResult(&TruncateFile), file_));
+ PostIOTask(FROM_HERE, base::Bind(base::IgnoreResult(&TruncateFile), file_));
return true;
}
@@ -673,11 +677,7 @@ void VisitedLinkMaster::FreeURLTable() {
}
if (!file_)
return;
-
- BrowserThread::PostTask(
- BrowserThread::FILE,
- FROM_HERE,
- base::Bind(base::IgnoreResult(&fclose), file_));
+ PostIOTask(FROM_HERE, base::Bind(base::IgnoreResult(&fclose), file_));
}
bool VisitedLinkMaster::ResizeTableIfNecessary() {
@@ -860,9 +860,7 @@ void VisitedLinkMaster::WriteToFile(FILE* file,
#ifndef NDEBUG
posted_asynchronous_operation_ = true;
#endif
-
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
+ PostIOTask(FROM_HERE,
base::Bind(&AsyncWrite, file, offset,
std::string(static_cast<const char*>(data), data_size)));
}
diff --git a/chrome/browser/visitedlink/visitedlink_master.h b/chrome/browser/visitedlink/visitedlink_master.h
index c84e809..1771afe 100644
--- a/chrome/browser/visitedlink/visitedlink_master.h
+++ b/chrome/browser/visitedlink/visitedlink_master.h
@@ -17,6 +17,7 @@
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/shared_memory.h"
+#include "base/threading/sequenced_worker_pool.h"
#include "chrome/browser/history/history.h"
#include "chrome/common/visitedlink_common.h"
@@ -163,6 +164,10 @@ class VisitedLinkMaster : public VisitedLinkCommon {
// File I/O functions
// ------------------
+ // Posts the given task to the blocking worker pool with our options.
+ void PostIOTask(const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
// Writes the entire table to disk, returning true on success. It will leave
// the table file open and the handle to it in file_
bool WriteFullTable();
@@ -312,6 +317,9 @@ class VisitedLinkMaster : public VisitedLinkCommon {
// (it knows the path to where the data is stored)
Profile* profile_;
+ // Lazily initialized sequence token for posting file tasks.
+ base::SequencedWorkerPool::SequenceToken sequence_token_;
+
// When non-NULL, indicates we are in database rebuild mode and points to
// the class collecting fingerprint information from the history system.
// The pointer is owned by this class, but it must remain valid while the
diff --git a/chrome/browser/visitedlink/visitedlink_unittest.cc b/chrome/browser/visitedlink/visitedlink_unittest.cc
index 2d52cb0..536384f 100644
--- a/chrome/browser/visitedlink/visitedlink_unittest.cc
+++ b/chrome/browser/visitedlink/visitedlink_unittest.cc
@@ -116,6 +116,9 @@ class VisitedLinkTest : public testing::Test {
// our destroy task.
MessageLoop::current()->Run();
}
+
+ // Wait for all pending file I/O to be completed.
+ BrowserThread::GetBlockingPool()->FlushForTesting();
}
// Loads the database from disk and makes sure that the same URLs are present
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 4c307b1..8203333 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -513,6 +513,14 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() {
}
}
+ // Close the blocking I/O pool after the other threads. Other threads such
+ // as the I/O thread may need to schedule work like closing files or flushing
+ // data during shutdown, so the blocking pool needs to be available. There
+ // may also be slow operations pending that will blcok shutdown, so closing
+ // it here (which will block until required operations are complete) gives
+ // more head start for those operations to finish.
+ BrowserThreadImpl::ShutdownThreadPool();
+
if (parts_.get())
parts_->PostDestroyThreads();
}
diff --git a/content/browser/browser_thread_impl.cc b/content/browser/browser_thread_impl.cc
index f7cca04..bd6aff9 100644
--- a/content/browser/browser_thread_impl.cc
+++ b/content/browser/browser_thread_impl.cc
@@ -9,6 +9,7 @@
#include "base/lazy_instance.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
+#include "base/threading/sequenced_worker_pool.h"
#include "base/threading/thread_restrictions.h"
namespace content {
@@ -27,24 +28,37 @@ static const char* g_browser_thread_names[BrowserThread::ID_COUNT] = {
"Chrome_IOThread", // IO
};
-// This lock protects |g_browser_threads|. Do not read or modify that
-// array without holding this lock. Do not block while holding this
-// lock.
-base::LazyInstance<base::Lock,
- base::LeakyLazyInstanceTraits<base::Lock> >
- g_lock = LAZY_INSTANCE_INITIALIZER;
-
-// This array is protected by |g_lock|. The threads are not owned by this
-// array. Typically, the threads are owned on the UI thread by
-// content::BrowserMainLoop. BrowserThreadImpl objects remove
-// themselves from this array upon destruction.
-static BrowserThreadImpl* g_browser_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.
-static BrowserThreadDelegate* g_browser_thread_delegates[
- BrowserThread::ID_COUNT];
+struct BrowserThreadGlobals {
+ BrowserThreadGlobals()
+ : blocking_pool(new base::SequencedWorkerPool(3, "BrowserBlocking")) {
+ memset(threads, 0,
+ BrowserThread::ID_COUNT * sizeof(BrowserThreadImpl*));
+ memset(thread_delegates, 0,
+ BrowserThread::ID_COUNT * sizeof(BrowserThreadDelegate*));
+ }
+
+ // 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
+ // content::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];
+
+ // This pointer is deliberately leaked on shutdown. This allows the pool to
+ // implement "continue on shutdown" semantics.
+ base::SequencedWorkerPool* blocking_pool;
+};
+
+base::LazyInstance<BrowserThreadGlobals,
+ base::LeakyLazyInstanceTraits<BrowserThreadGlobals> >
+ g_globals = LAZY_INSTANCE_INITIALIZER;
} // namespace
@@ -62,10 +76,18 @@ BrowserThreadImpl::BrowserThreadImpl(ID identifier,
Initialize();
}
+// static
+void BrowserThreadImpl::ShutdownThreadPool() {
+ BrowserThreadGlobals& globals = g_globals.Get();
+ globals.blocking_pool->Shutdown();
+}
+
void BrowserThreadImpl::Init() {
+ BrowserThreadGlobals& globals = g_globals.Get();
+
using base::subtle::AtomicWord;
AtomicWord* storage =
- reinterpret_cast<AtomicWord*>(&g_browser_thread_delegates[identifier_]);
+ reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]);
AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage);
BrowserThreadDelegate* delegate =
reinterpret_cast<BrowserThreadDelegate*>(stored_pointer);
@@ -74,9 +96,11 @@ void BrowserThreadImpl::Init() {
}
void BrowserThreadImpl::CleanUp() {
+ BrowserThreadGlobals& globals = g_globals.Get();
+
using base::subtle::AtomicWord;
AtomicWord* storage =
- reinterpret_cast<AtomicWord*>(&g_browser_thread_delegates[identifier_]);
+ reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]);
AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage);
BrowserThreadDelegate* delegate =
reinterpret_cast<BrowserThreadDelegate*>(stored_pointer);
@@ -86,10 +110,12 @@ void BrowserThreadImpl::CleanUp() {
}
void BrowserThreadImpl::Initialize() {
- base::AutoLock lock(g_lock.Get());
+ BrowserThreadGlobals& globals = g_globals.Get();
+
+ base::AutoLock lock(globals.lock);
DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT);
- DCHECK(g_browser_threads[identifier_] == NULL);
- g_browser_threads[identifier_] = this;
+ DCHECK(globals.threads[identifier_] == NULL);
+ globals.threads[identifier_] = this;
}
BrowserThreadImpl::~BrowserThreadImpl() {
@@ -98,12 +124,13 @@ BrowserThreadImpl::~BrowserThreadImpl() {
// the right BrowserThread.
Stop();
- base::AutoLock lock(g_lock.Get());
- g_browser_threads[identifier_] = NULL;
+ 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(!g_browser_threads[i]) <<
+ DCHECK(!globals.threads[i]) <<
"Threads must be listed in the reverse order that they die";
}
#endif
@@ -128,11 +155,12 @@ bool BrowserThreadImpl::PostTaskHelper(
GetCurrentThreadIdentifier(&current_thread) &&
current_thread <= identifier;
+ BrowserThreadGlobals& globals = g_globals.Get();
if (!guaranteed_to_outlive_target_thread)
- g_lock.Get().Acquire();
+ globals.lock.Acquire();
- MessageLoop* message_loop = g_browser_threads[identifier] ?
- g_browser_threads[identifier]->message_loop() : NULL;
+ MessageLoop* message_loop = globals.threads[identifier] ?
+ globals.threads[identifier]->message_loop() : NULL;
if (message_loop) {
if (nestable) {
message_loop->PostDelayedTask(from_here, task, delay_ms);
@@ -142,7 +170,7 @@ bool BrowserThreadImpl::PostTaskHelper(
}
if (!guaranteed_to_outlive_target_thread)
- g_lock.Get().Release();
+ globals.lock.Release();
return !!message_loop;
}
@@ -189,10 +217,32 @@ class BrowserThreadMessageLoopProxy : public base::MessageLoopProxy {
};
// static
+bool BrowserThread::PostBlockingPoolTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ return g_globals.Get().blocking_pool->PostWorkerTask(from_here, task);
+}
+
+// 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;
+}
+
+// static
bool BrowserThread::IsWellKnownThread(ID identifier) {
- base::AutoLock lock(g_lock.Get());
+ BrowserThreadGlobals& globals = g_globals.Get();
+ base::AutoLock lock(globals.lock);
return (identifier >= 0 && identifier < ID_COUNT &&
- g_browser_threads[identifier]);
+ globals.threads[identifier]);
}
// static
@@ -202,19 +252,21 @@ bool BrowserThread::CurrentlyOn(ID identifier) {
// function.
// http://crbug.com/63678
base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
- base::AutoLock lock(g_lock.Get());
+ BrowserThreadGlobals& globals = g_globals.Get();
+ base::AutoLock lock(globals.lock);
DCHECK(identifier >= 0 && identifier < ID_COUNT);
- return g_browser_threads[identifier] &&
- g_browser_threads[identifier]->message_loop() ==
+ return globals.threads[identifier] &&
+ globals.threads[identifier]->message_loop() ==
MessageLoop::current();
}
// static
bool BrowserThread::IsMessageLoopValid(ID identifier) {
- base::AutoLock lock(g_lock.Get());
+ BrowserThreadGlobals& globals = g_globals.Get();
+ base::AutoLock lock(globals.lock);
DCHECK(identifier >= 0 && identifier < ID_COUNT);
- return g_browser_threads[identifier] &&
- g_browser_threads[identifier]->message_loop();
+ return globals.threads[identifier] &&
+ globals.threads[identifier]->message_loop();
}
// static
@@ -272,10 +324,11 @@ bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) {
// http://crbug.com/63678
base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
MessageLoop* cur_message_loop = MessageLoop::current();
+ BrowserThreadGlobals& globals = g_globals.Get();
for (int i = 0; i < ID_COUNT; ++i) {
- if (g_browser_threads[i] &&
- g_browser_threads[i]->message_loop() == cur_message_loop) {
- *identifier = g_browser_threads[i]->identifier_;
+ if (globals.threads[i] &&
+ globals.threads[i]->message_loop() == cur_message_loop) {
+ *identifier = globals.threads[i]->identifier_;
return true;
}
}
@@ -285,8 +338,7 @@ bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) {
// static
scoped_refptr<base::MessageLoopProxy>
-BrowserThread::GetMessageLoopProxyForThread(
- ID identifier) {
+BrowserThread::GetMessageLoopProxyForThread(ID identifier) {
scoped_refptr<base::MessageLoopProxy> proxy(
new BrowserThreadMessageLoopProxy(identifier));
return proxy;
@@ -294,8 +346,9 @@ BrowserThread::GetMessageLoopProxyForThread(
// static
MessageLoop* BrowserThread::UnsafeGetMessageLoopForThread(ID identifier) {
- base::AutoLock lock(g_lock.Get());
- base::Thread* thread = g_browser_threads[identifier];
+ BrowserThreadGlobals& globals = g_globals.Get();
+ base::AutoLock lock(globals.lock);
+ base::Thread* thread = globals.threads[identifier];
DCHECK(thread);
MessageLoop* loop = thread->message_loop();
return loop;
@@ -305,8 +358,9 @@ MessageLoop* BrowserThread::UnsafeGetMessageLoopForThread(ID identifier) {
void BrowserThread::SetDelegate(ID identifier,
BrowserThreadDelegate* delegate) {
using base::subtle::AtomicWord;
+ BrowserThreadGlobals& globals = g_globals.Get();
AtomicWord* storage = reinterpret_cast<AtomicWord*>(
- &g_browser_thread_delegates[identifier]);
+ &globals.thread_delegates[identifier]);
AtomicWord old_pointer = base::subtle::NoBarrier_AtomicExchange(
storage, reinterpret_cast<AtomicWord>(delegate));
diff --git a/content/browser/browser_thread_impl.h b/content/browser/browser_thread_impl.h
index feea986..4488f85 100644
--- a/content/browser/browser_thread_impl.h
+++ b/content/browser/browser_thread_impl.h
@@ -25,6 +25,8 @@ class CONTENT_EXPORT BrowserThreadImpl
BrowserThreadImpl(BrowserThread::ID identifier, MessageLoop* message_loop);
virtual ~BrowserThreadImpl();
+ static void ShutdownThreadPool();
+
protected:
virtual void Init() OVERRIDE;
virtual void CleanUp() OVERRIDE;
diff --git a/content/public/browser/browser_thread.h b/content/public/browser/browser_thread.h
index d106929..f35ec00 100644
--- a/content/public/browser/browser_thread.h
+++ b/content/public/browser/browser_thread.h
@@ -20,6 +20,7 @@
class MessageLoop;
namespace base {
+class SequencedWorkerPool;
class Thread;
}
@@ -137,6 +138,34 @@ class CONTENT_EXPORT BrowserThread {
from_here, object);
}
+ // Simplified wrappers for posting to the blocking thread pool. Use this
+ // for doing things like blocking I/O.
+ //
+ // The first variant will run the task in the pool with no sequencing
+ // semantics, so may get run in parallel with other posted tasks. The
+ // second variant provides sequencing between tasks with the same
+ // sequence token name.
+ //
+ // These tasks are guaranteed to run before shutdown.
+ //
+ // If you need to provide different shutdown semantics (like you have
+ // something slow and noncritical that doesn't need to block shutdown),
+ // or you want to manually provide a sequence token (which saves a map
+ // lookup and is guaranteed unique without you having to come up with a
+ // unique string), you can access the sequenced worker pool directly via
+ // GetBlockingPool().
+ static bool PostBlockingPoolTask(const tracked_objects::Location& from_here,
+ const base::Closure& task);
+ static bool PostBlockingPoolSequencedTask(
+ const std::string& sequence_token_name,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
+ // Returns the thread pool used for blocking file I/O. Use this object to
+ // perform random blocking operations such as file writes or querying the
+ // Windows registry.
+ static base::SequencedWorkerPool* GetBlockingPool();
+
// Callable on any thread. Returns whether the given ID corresponds to a well
// known thread.
static bool IsWellKnownThread(ID identifier);