Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/browser_thread_impl.h" | 5 #include "content/browser/browser_thread_impl.h" |
| 6 | 6 |
| 7 #include <string.h> | |
| 8 | |
| 9 #include <string> | 7 #include <string> |
| 10 | 8 |
| 11 #include "base/atomicops.h" | 9 #include "base/atomicops.h" |
| 12 #include "base/bind.h" | 10 #include "base/bind.h" |
| 13 #include "base/compiler_specific.h" | 11 #include "base/compiler_specific.h" |
| 14 #include "base/lazy_instance.h" | 12 #include "base/lazy_instance.h" |
| 13 #include "base/logging.h" | |
| 15 #include "base/macros.h" | 14 #include "base/macros.h" |
| 15 #include "base/message_loop/message_loop.h" | |
| 16 #include "base/profiler/scoped_tracker.h" | 16 #include "base/profiler/scoped_tracker.h" |
| 17 #include "base/run_loop.h" | 17 #include "base/run_loop.h" |
| 18 #include "base/single_thread_task_runner.h" | 18 #include "base/synchronization/waitable_event.h" |
| 19 #include "base/threading/platform_thread.h" | 19 #include "base/threading/platform_thread.h" |
| 20 #include "base/threading/sequenced_worker_pool.h" | 20 #include "base/threading/sequenced_worker_pool.h" |
| 21 #include "build/build_config.h" | 21 #include "build/build_config.h" |
| 22 #include "content/public/browser/browser_thread_delegate.h" | 22 #include "content/public/browser/browser_thread_delegate.h" |
| 23 #include "content/public/browser/content_browser_client.h" | 23 #include "content/public/browser/content_browser_client.h" |
| 24 #include "net/disk_cache/simple/simple_backend_impl.h" | 24 #include "net/disk_cache/simple/simple_backend_impl.h" |
| 25 | 25 |
| 26 #if defined(OS_ANDROID) | 26 #if defined(OS_ANDROID) |
| 27 #include "base/android/jni_android.h" | 27 #include "base/android/jni_android.h" |
| 28 #endif | 28 #endif |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 45 static const char* GetThreadName(BrowserThread::ID thread) { | 45 static const char* GetThreadName(BrowserThread::ID thread) { |
| 46 if (BrowserThread::UI < thread && thread < BrowserThread::ID_COUNT) | 46 if (BrowserThread::UI < thread && thread < BrowserThread::ID_COUNT) |
| 47 return g_browser_thread_names[thread]; | 47 return g_browser_thread_names[thread]; |
| 48 if (thread == BrowserThread::UI) | 48 if (thread == BrowserThread::UI) |
| 49 return "Chrome_UIThread"; | 49 return "Chrome_UIThread"; |
| 50 return "Unknown Thread"; | 50 return "Unknown Thread"; |
| 51 } | 51 } |
| 52 | 52 |
| 53 // An implementation of SingleThreadTaskRunner to be used in conjunction | 53 // An implementation of SingleThreadTaskRunner to be used in conjunction |
| 54 // with BrowserThread. | 54 // with BrowserThread. |
| 55 // TODO(gab): Consider replacing this with |g_globals->task_runners| -- only | |
| 56 // works if none are requested before starting the threads. | |
| 55 class BrowserThreadTaskRunner : public base::SingleThreadTaskRunner { | 57 class BrowserThreadTaskRunner : public base::SingleThreadTaskRunner { |
| 56 public: | 58 public: |
| 57 explicit BrowserThreadTaskRunner(BrowserThread::ID identifier) | 59 explicit BrowserThreadTaskRunner(BrowserThread::ID identifier) |
| 58 : id_(identifier) {} | 60 : id_(identifier) {} |
| 59 | 61 |
| 60 // SingleThreadTaskRunner implementation. | 62 // SingleThreadTaskRunner implementation. |
| 61 bool PostDelayedTask(const tracked_objects::Location& from_here, | 63 bool PostDelayedTask(const tracked_objects::Location& from_here, |
| 62 const base::Closure& task, | 64 const base::Closure& task, |
| 63 base::TimeDelta delay) override { | 65 base::TimeDelta delay) override { |
| 64 return BrowserThread::PostDelayedTask(id_, from_here, task, delay); | 66 return BrowserThread::PostDelayedTask(id_, from_here, task, delay); |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 92 new BrowserThreadTaskRunner(static_cast<BrowserThread::ID>(i)); | 94 new BrowserThreadTaskRunner(static_cast<BrowserThread::ID>(i)); |
| 93 } | 95 } |
| 94 } | 96 } |
| 95 | 97 |
| 96 scoped_refptr<base::SingleThreadTaskRunner> proxies[BrowserThread::ID_COUNT]; | 98 scoped_refptr<base::SingleThreadTaskRunner> proxies[BrowserThread::ID_COUNT]; |
| 97 }; | 99 }; |
| 98 | 100 |
| 99 base::LazyInstance<BrowserThreadTaskRunners>::Leaky g_task_runners = | 101 base::LazyInstance<BrowserThreadTaskRunners>::Leaky g_task_runners = |
| 100 LAZY_INSTANCE_INITIALIZER; | 102 LAZY_INSTANCE_INITIALIZER; |
| 101 | 103 |
| 104 // State of a given BrowserThread::ID in chronological order throughout the | |
| 105 // browser process' lifetime. | |
| 106 enum BrowserThreadState { | |
| 107 // BrowserThread::ID isn't associated with anything yet. | |
| 108 UNINITIALIZED = 0, | |
| 109 // BrowserThread::ID is associated with a BrowserThreadImpl instance but the | |
| 110 // underlying thread hasn't started yet. | |
| 111 INITIALIZED, | |
| 112 // BrowserThread::ID is associated to a TaskRunner and is accepting tasks. | |
| 113 RUNNING, | |
| 114 // BrowserThread::ID no longer accepts tasks. | |
| 115 SHUTDOWN | |
| 116 }; | |
| 117 | |
| 102 using BrowserThreadDelegateAtomicPtr = base::subtle::AtomicWord; | 118 using BrowserThreadDelegateAtomicPtr = base::subtle::AtomicWord; |
| 103 | 119 |
| 104 struct BrowserThreadGlobals { | 120 struct BrowserThreadGlobals { |
| 105 BrowserThreadGlobals() | 121 BrowserThreadGlobals() |
| 106 : blocking_pool( | 122 : blocking_pool( |
| 107 new base::SequencedWorkerPool(3, | 123 new base::SequencedWorkerPool(3, |
| 108 "BrowserBlocking", | 124 "BrowserBlocking", |
| 109 base::TaskPriority::USER_VISIBLE)) { | 125 base::TaskPriority::USER_VISIBLE)) {} |
| 110 memset(threads, 0, BrowserThread::ID_COUNT * sizeof(threads[0])); | |
| 111 memset(thread_ids, 0, BrowserThread::ID_COUNT * sizeof(thread_ids[0])); | |
| 112 } | |
| 113 | 126 |
| 114 // This lock protects |threads| and |thread_ids|. Do not read or modify those | 127 // This lock protects |task_runners| and |states|. Do not read or modify those |
| 115 // arrays without holding this lock. Do not block while holding this lock. | 128 // arrays without holding this lock. Do not block while holding this lock. |
| 116 base::Lock lock; | 129 base::Lock lock; |
| 117 | 130 |
| 118 // This array is protected by |lock|. IDs in this array are populated as soon | 131 // This array is filled either as the underlying threads start and invoke |
| 119 // as their respective thread is started and are never reset. | 132 // Init() or in RedirectThreadIDToTaskRunner() for threads that are being |
| 120 base::PlatformThreadId thread_ids[BrowserThread::ID_COUNT]; | 133 // redirected. It is not emptied during shutdown in order to support |
| 134 // RunsTasksOnCurrentThread() until the very end. | |
| 135 scoped_refptr<base::SingleThreadTaskRunner> | |
| 136 task_runners[BrowserThread::ID_COUNT]; | |
| 121 | 137 |
| 122 // This array is protected by |lock|. The threads are not owned by this | 138 // Holds the state of each BrowserThread::ID. |
| 123 // array. Typically, the threads are owned on the UI thread by | 139 BrowserThreadState states[BrowserThread::ID_COUNT] = {}; |
| 124 // BrowserMainLoop. BrowserThreadImpl objects remove themselves from this | |
| 125 // array upon destruction. | |
| 126 BrowserThreadImpl* threads[BrowserThread::ID_COUNT]; | |
| 127 | 140 |
| 128 // Only atomic operations are used on this pointer. The delegate isn't owned | 141 // Only atomic operations are used on this pointer. The delegate isn't owned |
| 129 // by BrowserThreadGlobals, rather by whoever calls | 142 // by BrowserThreadGlobals, rather by whoever calls |
| 130 // BrowserThread::SetIOThreadDelegate. | 143 // BrowserThread::SetIOThreadDelegate. |
| 131 BrowserThreadDelegateAtomicPtr io_thread_delegate = 0; | 144 BrowserThreadDelegateAtomicPtr io_thread_delegate = 0; |
| 132 | 145 |
| 133 const scoped_refptr<base::SequencedWorkerPool> blocking_pool; | 146 const scoped_refptr<base::SequencedWorkerPool> blocking_pool; |
| 134 }; | 147 }; |
| 135 | 148 |
| 136 base::LazyInstance<BrowserThreadGlobals>::Leaky | 149 base::LazyInstance<BrowserThreadGlobals>::Leaky |
| 137 g_globals = LAZY_INSTANCE_INITIALIZER; | 150 g_globals = LAZY_INSTANCE_INITIALIZER; |
| 138 | 151 |
| 139 } // namespace | 152 } // namespace |
| 140 | 153 |
| 141 BrowserThreadImpl::BrowserThreadImpl(ID identifier) | 154 BrowserThreadImpl::BrowserThreadImpl(ID identifier) |
| 142 : Thread(GetThreadName(identifier)), identifier_(identifier) { | 155 : Thread(GetThreadName(identifier)), identifier_(identifier) { |
| 143 Initialize(); | 156 Initialize(); |
| 144 | |
| 145 // Unit tests may create multiple TestBrowserThreadBundles, causing the same | |
| 146 // BrowserThread ID to be reinitialized. We explicitly clear the thread ID | |
| 147 // here so Start() can sanity check. | |
| 148 BrowserThreadGlobals& globals = g_globals.Get(); | |
| 149 base::AutoLock lock(globals.lock); | |
| 150 globals.thread_ids[identifier] = base::kInvalidThreadId; | |
| 151 } | 157 } |
| 152 | 158 |
| 153 BrowserThreadImpl::BrowserThreadImpl(ID identifier, | 159 BrowserThreadImpl::BrowserThreadImpl(ID identifier, |
| 154 base::MessageLoop* message_loop) | 160 base::MessageLoop* message_loop) |
| 155 : Thread(GetThreadName(identifier)), identifier_(identifier) { | 161 : Thread(GetThreadName(identifier)), identifier_(identifier) { |
| 156 SetMessageLoop(message_loop); | 162 SetMessageLoop(message_loop); |
| 157 Initialize(); | 163 Initialize(); |
| 158 | 164 |
| 159 // If constructed with an explicit message loop, this is a fake BrowserThread | 165 // If constructed with an explicit message loop, this is a fake |
| 160 // which runs on the current thread. | 166 // BrowserThread which runs on the current thread. |
| 161 BrowserThreadGlobals& globals = g_globals.Get(); | 167 BrowserThreadGlobals& globals = g_globals.Get(); |
| 162 base::AutoLock lock(globals.lock); | 168 base::AutoLock lock(globals.lock); |
| 163 globals.thread_ids[identifier] = base::PlatformThread::CurrentId(); | 169 |
| 170 DCHECK(!globals.task_runners[identifier_]); | |
| 171 globals.task_runners[identifier_] = task_runner(); | |
| 172 | |
| 173 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::INITIALIZED); | |
| 174 globals.states[identifier_] = BrowserThreadState::RUNNING; | |
| 164 } | 175 } |
| 165 | 176 |
| 166 // static | 177 // static |
| 167 void BrowserThreadImpl::ShutdownThreadPool() { | 178 void BrowserThreadImpl::ShutdownThreadPool() { |
| 168 // The goal is to make it impossible for chrome to 'infinite loop' during | 179 // The goal is to make it impossible for chrome to 'infinite loop' during |
| 169 // shutdown, but to reasonably expect that all BLOCKING_SHUTDOWN tasks queued | 180 // shutdown, but to reasonably expect that all BLOCKING_SHUTDOWN tasks queued |
| 170 // during shutdown get run. There's nothing particularly scientific about the | 181 // during shutdown get run. There's nothing particularly scientific about the |
| 171 // number chosen. | 182 // number chosen. |
| 172 const int kMaxNewShutdownBlockingTasks = 1000; | 183 const int kMaxNewShutdownBlockingTasks = 1000; |
| 173 BrowserThreadGlobals& globals = g_globals.Get(); | 184 BrowserThreadGlobals& globals = g_globals.Get(); |
| 174 globals.blocking_pool->Shutdown(kMaxNewShutdownBlockingTasks); | 185 globals.blocking_pool->Shutdown(kMaxNewShutdownBlockingTasks); |
| 175 } | 186 } |
| 176 | 187 |
| 177 // static | 188 // static |
| 178 void BrowserThreadImpl::FlushThreadPoolHelperForTesting() { | 189 void BrowserThreadImpl::FlushThreadPoolHelperForTesting() { |
| 179 // We don't want to create a pool if none exists. | 190 // We don't want to create a pool if none exists. |
| 180 if (g_globals == nullptr) | 191 if (g_globals == nullptr) |
| 181 return; | 192 return; |
| 182 g_globals.Get().blocking_pool->FlushForTesting(); | 193 g_globals.Get().blocking_pool->FlushForTesting(); |
| 183 disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting(); | 194 disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting(); |
| 184 } | 195 } |
| 185 | 196 |
| 186 void BrowserThreadImpl::Init() { | 197 void BrowserThreadImpl::Init() { |
| 187 BrowserThreadGlobals& globals = g_globals.Get(); | 198 BrowserThreadGlobals& globals = g_globals.Get(); |
| 188 | 199 |
| 200 #if DCHECK_IS_ON() | |
| 201 { | |
| 202 base::AutoLock lock(globals.lock); | |
| 203 // |globals| should already have been initialized for |identifier_| in | |
| 204 // BrowserThreadImpl::StartWithOptions(). If this isn't the case it's likely | |
| 205 // because this BrowserThreadImpl's owner incorrectly used Thread::Start.*() | |
| 206 // instead of BrowserThreadImpl::Start.*(). | |
| 207 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::RUNNING); | |
| 208 DCHECK(globals.task_runners[identifier_]); | |
| 209 DCHECK(globals.task_runners[identifier_]->RunsTasksOnCurrentThread()); | |
| 210 } | |
| 211 #endif // DCHECK_IS_ON() | |
| 212 | |
| 189 if (identifier_ == BrowserThread::DB || | 213 if (identifier_ == BrowserThread::DB || |
| 190 identifier_ == BrowserThread::FILE || | 214 identifier_ == BrowserThread::FILE || |
| 191 identifier_ == BrowserThread::FILE_USER_BLOCKING || | 215 identifier_ == BrowserThread::FILE_USER_BLOCKING || |
| 192 identifier_ == BrowserThread::PROCESS_LAUNCHER || | 216 identifier_ == BrowserThread::PROCESS_LAUNCHER || |
| 193 identifier_ == BrowserThread::CACHE) { | 217 identifier_ == BrowserThread::CACHE) { |
| 194 base::MessageLoop* message_loop = base::MessageLoop::current(); | 218 // Nesting and task observers are not allowed on redirected threads. |
| 195 message_loop->DisallowNesting(); | 219 message_loop()->DisallowNesting(); |
| 196 message_loop->DisallowTaskObservers(); | 220 message_loop()->DisallowTaskObservers(); |
| 197 } | 221 } |
| 198 | 222 |
| 199 if (identifier_ == BrowserThread::IO) { | 223 if (identifier_ == BrowserThread::IO) { |
| 200 BrowserThreadDelegateAtomicPtr delegate = | 224 BrowserThreadDelegateAtomicPtr delegate = |
| 201 base::subtle::NoBarrier_Load(&globals.io_thread_delegate); | 225 base::subtle::NoBarrier_Load(&globals.io_thread_delegate); |
| 202 if (delegate) | 226 if (delegate) |
| 203 reinterpret_cast<BrowserThreadDelegate*>(delegate)->Init(); | 227 reinterpret_cast<BrowserThreadDelegate*>(delegate)->Init(); |
| 204 } | 228 } |
| 205 } | 229 } |
| 206 | 230 |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 298 void BrowserThreadImpl::CleanUp() { | 322 void BrowserThreadImpl::CleanUp() { |
| 299 BrowserThreadGlobals& globals = g_globals.Get(); | 323 BrowserThreadGlobals& globals = g_globals.Get(); |
| 300 | 324 |
| 301 if (identifier_ == BrowserThread::IO) { | 325 if (identifier_ == BrowserThread::IO) { |
| 302 BrowserThreadDelegateAtomicPtr delegate = | 326 BrowserThreadDelegateAtomicPtr delegate = |
| 303 base::subtle::NoBarrier_Load(&globals.io_thread_delegate); | 327 base::subtle::NoBarrier_Load(&globals.io_thread_delegate); |
| 304 if (delegate) | 328 if (delegate) |
| 305 reinterpret_cast<BrowserThreadDelegate*>(delegate)->CleanUp(); | 329 reinterpret_cast<BrowserThreadDelegate*>(delegate)->CleanUp(); |
| 306 } | 330 } |
| 307 | 331 |
| 308 // PostTaskHelper() accesses the message loop while holding this lock. | 332 // Change the state to SHUTDOWN so that PostTaskHelper stops accepting tasks |
| 309 // However, the message loop will soon be destructed without any locking. So | 333 // for this thread. Do not clear globals.task_runners[identifier_] so that |
| 310 // to prevent a race with accessing the message loop in PostTaskHelper(), | 334 // BrowserThread::CurrentlyOn() works from the MessageLoop's |
| 311 // remove this thread from the global array now. | 335 // DestructionObservers. |
| 312 base::AutoLock lock(globals.lock); | 336 base::AutoLock lock(globals.lock); |
| 313 globals.threads[identifier_] = nullptr; | 337 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::RUNNING); |
| 338 globals.states[identifier_] = BrowserThreadState::SHUTDOWN; | |
| 314 } | 339 } |
| 315 | 340 |
| 316 void BrowserThreadImpl::Initialize() { | 341 void BrowserThreadImpl::Initialize() { |
| 317 BrowserThreadGlobals& globals = g_globals.Get(); | 342 BrowserThreadGlobals& globals = g_globals.Get(); |
| 318 | 343 |
| 319 base::AutoLock lock(globals.lock); | 344 base::AutoLock lock(globals.lock); |
| 320 DCHECK_GE(identifier_, 0); | 345 DCHECK_GE(identifier_, 0); |
| 321 DCHECK_LT(identifier_, ID_COUNT); | 346 DCHECK_LT(identifier_, ID_COUNT); |
| 322 DCHECK_EQ(globals.threads[identifier_], nullptr); | 347 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::UNINITIALIZED); |
| 323 globals.threads[identifier_] = this; | 348 globals.states[identifier_] = BrowserThreadState::INITIALIZED; |
| 349 } | |
| 350 | |
| 351 // static | |
| 352 void BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::ID identifier) { | |
| 353 BrowserThreadGlobals& globals = g_globals.Get(); | |
| 354 | |
| 355 base::AutoLock lock(globals.lock); | |
| 356 DCHECK_EQ(globals.states[identifier], BrowserThreadState::SHUTDOWN); | |
| 357 globals.states[identifier] = BrowserThreadState::UNINITIALIZED; | |
| 358 globals.task_runners[identifier] = nullptr; | |
| 359 SetDelegate(identifier, nullptr); | |
| 324 } | 360 } |
| 325 | 361 |
| 326 BrowserThreadImpl::~BrowserThreadImpl() { | 362 BrowserThreadImpl::~BrowserThreadImpl() { |
| 327 // All Thread subclasses must call Stop() in the destructor. This is | 363 // All Thread subclasses must call Stop() in the destructor. This is |
| 328 // doubly important here as various bits of code check they are on | 364 // doubly important here as various bits of code check they are on |
| 329 // the right BrowserThread. | 365 // the right BrowserThread. |
| 330 Stop(); | 366 Stop(); |
| 331 | 367 |
| 332 BrowserThreadGlobals& globals = g_globals.Get(); | 368 BrowserThreadGlobals& globals = g_globals.Get(); |
| 333 base::AutoLock lock(globals.lock); | 369 base::AutoLock lock(globals.lock); |
| 334 globals.threads[identifier_] = nullptr; | 370 // This thread should have gone through Cleanup() as part of Stop() and be in |
| 335 #ifndef NDEBUG | 371 // the SHUTDOWN state already (unless it uses an externally provided |
| 372 // MessageLoop instead of a real underlying thread and thus doesn't go through | |
| 373 // Cleanup()). | |
| 374 if (using_external_message_loop()) { | |
| 375 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::RUNNING); | |
| 376 globals.states[identifier_] = BrowserThreadState::SHUTDOWN; | |
| 377 } else { | |
| 378 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::SHUTDOWN); | |
| 379 } | |
| 380 #if DCHECK_IS_ON() | |
| 336 // Double check that the threads are ordered correctly in the enumeration. | 381 // Double check that the threads are ordered correctly in the enumeration. |
| 337 for (int i = identifier_ + 1; i < ID_COUNT; ++i) { | 382 for (int i = identifier_ + 1; i < ID_COUNT; ++i) { |
| 338 DCHECK(!globals.threads[i]) << | 383 DCHECK(globals.states[i] == BrowserThreadState::SHUTDOWN || |
| 339 "Threads must be listed in the reverse order that they die"; | 384 globals.states[i] == BrowserThreadState::UNINITIALIZED) |
| 385 << "Threads must be listed in the reverse order that they die"; | |
| 340 } | 386 } |
| 341 #endif | 387 #endif |
| 342 } | 388 } |
| 343 | 389 |
| 344 bool BrowserThreadImpl::Start() { | 390 bool BrowserThreadImpl::Start() { |
| 345 return StartWithOptions(base::Thread::Options()); | 391 return StartWithOptions(base::Thread::Options()); |
| 346 } | 392 } |
| 347 | 393 |
| 348 bool BrowserThreadImpl::StartWithOptions(const Options& options) { | 394 bool BrowserThreadImpl::StartWithOptions(const Options& options) { |
| 349 // The global thread table needs to be locked while a new thread is | |
| 350 // starting, as the new thread can asynchronously start touching the | |
| 351 // table (and other thread's message_loop). | |
| 352 BrowserThreadGlobals& globals = g_globals.Get(); | 395 BrowserThreadGlobals& globals = g_globals.Get(); |
| 396 | |
| 397 // Holding the lock is necessary when kicking off the thread to ensure | |
| 398 // |states| and |task_runners| are updated before it gets to query them. | |
| 353 base::AutoLock lock(globals.lock); | 399 base::AutoLock lock(globals.lock); |
| 354 DCHECK_EQ(globals.thread_ids[identifier_], base::kInvalidThreadId); | 400 |
| 355 bool result = Thread::StartWithOptions(options); | 401 bool result = Thread::StartWithOptions(options); |
| 356 globals.thread_ids[identifier_] = GetThreadId(); | 402 |
| 403 // Although the thread is starting asynchronously. The MessageLoop is already | |
|
fdoray
2016/12/08 14:51:46
Although the thread is starting asynchronously <<,
gab
2016/12/08 18:49:41
Done.
| |
| 404 // ready to accept tasks and as such this BrowserThreadImpl is considered as | |
| 405 // "running". | |
| 406 DCHECK(!globals.task_runners[identifier_]); | |
| 407 globals.task_runners[identifier_] = task_runner(); | |
| 408 DCHECK(globals.task_runners[identifier_]); | |
| 409 | |
| 410 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::INITIALIZED); | |
| 411 globals.states[identifier_] = BrowserThreadState::RUNNING; | |
|
robliao
2016/12/08 01:57:17
In the previous code, if thread creation failed, w
fdoray
2016/12/08 14:51:46
This change could make startup faster!
gab
2016/12/08 18:49:41
Yes this is fine because browser_main_loop.cc does
robliao
2016/12/14 20:38:13
The state seems a little wonky given that we enter
| |
| 412 | |
| 357 return result; | 413 return result; |
| 358 } | 414 } |
| 359 | 415 |
| 360 bool BrowserThreadImpl::StartAndWaitForTesting() { | 416 bool BrowserThreadImpl::StartAndWaitForTesting() { |
| 361 if (!Start()) | 417 if (!Start()) |
| 362 return false; | 418 return false; |
| 363 WaitUntilThreadStarted(); | 419 WaitUntilThreadStarted(); |
| 364 return true; | 420 return true; |
| 365 } | 421 } |
| 422 | |
| 423 // static | |
| 424 void BrowserThreadImpl::RedirectThreadIDToTaskRunner( | |
| 425 BrowserThread::ID identifier, | |
| 426 scoped_refptr<base::SingleThreadTaskRunner> task_runner) { | |
| 427 DCHECK(task_runner); | |
| 428 | |
| 429 BrowserThreadGlobals& globals = g_globals.Get(); | |
| 430 base::AutoLock lock(globals.lock); | |
| 431 | |
| 432 DCHECK(!globals.task_runners[identifier]); | |
| 433 DCHECK_EQ(globals.states[identifier], BrowserThreadState::UNINITIALIZED); | |
| 434 | |
| 435 globals.task_runners[identifier] = std::move(task_runner); | |
| 436 globals.states[identifier] = BrowserThreadState::RUNNING; | |
| 437 } | |
| 438 | |
| 439 // static | |
| 440 void BrowserThreadImpl::StopRedirectionOfThreadID( | |
| 441 BrowserThread::ID identifier) { | |
| 442 BrowserThreadGlobals& globals = g_globals.Get(); | |
| 443 base::AutoLock auto_lock(globals.lock); | |
| 444 | |
| 445 DCHECK(globals.task_runners[identifier]); | |
| 446 | |
| 447 // Change the state to SHUTDOWN to stop accepting new tasks. Note: this is | |
| 448 // different from non-redirected threads which continue accepting tasks while | |
| 449 // being joined and only quit when idle. However, any tasks for which this | |
| 450 // difference matters was already racy as any thread posting a task after the | |
| 451 // Signal task below can't be synchronized with the joining thread. Therefore, | |
| 452 // that task could already come in before or after the join had completed in | |
| 453 // the non-redirection world. Entering SHUTDOWN early merely skews this race | |
| 454 // towards making it less likely such a task is accepted by the joined thread | |
| 455 // which is fine. | |
| 456 DCHECK_EQ(globals.states[identifier], BrowserThreadState::RUNNING); | |
| 457 globals.states[identifier] = BrowserThreadState::SHUTDOWN; | |
| 458 | |
| 459 // Wait for all pending tasks to complete. | |
| 460 base::WaitableEvent flushed(base::WaitableEvent::ResetPolicy::MANUAL, | |
| 461 base::WaitableEvent::InitialState::NOT_SIGNALED); | |
| 462 globals.task_runners[identifier]->PostTask( | |
| 463 FROM_HERE, | |
| 464 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&flushed))); | |
| 465 { | |
| 466 base::AutoUnlock auto_lock(globals.lock); | |
| 467 flushed.Wait(); | |
| 468 } | |
| 469 | |
| 470 // Only reset the task runner after running pending tasks so that | |
| 471 // BrowserThread::CurrentlyOn() works in their scope. | |
| 472 globals.task_runners[identifier] = nullptr; | |
| 473 | |
| 474 // Note: it's still possible for tasks to be posted to that task runner after | |
| 475 // this point (e.g. through a previously obtained ThreadTaskRunnerHandle or by | |
| 476 // one of the last tasks re-posting to its ThreadTaskRunnerHandle) but the | |
| 477 // BrowserThread API itself won't accept tasks. Such tasks are ultimately | |
| 478 // guaranteed to run before TaskScheduler::Shutdown() returns but may break | |
| 479 // the assumption in PostTaskHelper that BrowserThread::ID A > B will always | |
| 480 // succeed to post to B. This is pretty much the only observable difference | |
| 481 // between a redirected thread and a real one and is one we're willing to live | |
| 482 // with for this experiment. TODO(gab): fix this before enabling the | |
| 483 // experiment by default on trunk, http://crbug.com/653916. | |
| 484 } | |
| 485 | |
| 366 // static | 486 // static |
| 367 bool BrowserThreadImpl::PostTaskHelper( | 487 bool BrowserThreadImpl::PostTaskHelper( |
| 368 BrowserThread::ID identifier, | 488 BrowserThread::ID identifier, |
| 369 const tracked_objects::Location& from_here, | 489 const tracked_objects::Location& from_here, |
| 370 const base::Closure& task, | 490 const base::Closure& task, |
| 371 base::TimeDelta delay, | 491 base::TimeDelta delay, |
| 372 bool nestable) { | 492 bool nestable) { |
| 373 DCHECK_GE(identifier, 0); | 493 DCHECK_GE(identifier, 0); |
| 374 DCHECK_LT(identifier, ID_COUNT); | 494 DCHECK_LT(identifier, ID_COUNT); |
| 375 // Optimization: to avoid unnecessary locks, we listed the ID enumeration in | 495 // Optimization: to avoid unnecessary locks, we listed the ID enumeration in |
| 376 // order of lifetime. So no need to lock if we know that the target thread | 496 // order of lifetime. So no need to lock if we know that the target thread |
| 377 // outlives current thread. | 497 // outlives current thread as that implies the current thread only ever sees |
| 498 // the target thread in its RUNNING state. | |
| 378 // Note: since the array is so small, ok to loop instead of creating a map, | 499 // Note: since the array is so small, ok to loop instead of creating a map, |
| 379 // which would require a lock because std::map isn't thread safe, defeating | 500 // which would require a lock because std::map isn't thread safe, defeating |
| 380 // the whole purpose of this optimization. | 501 // the whole purpose of this optimization. |
| 381 BrowserThread::ID current_thread = ID_COUNT; | 502 BrowserThread::ID current_thread = ID_COUNT; |
| 382 bool target_thread_outlives_current = | 503 bool target_thread_outlives_current = |
| 383 GetCurrentThreadIdentifier(¤t_thread) && | 504 GetCurrentThreadIdentifier(¤t_thread) && |
| 384 current_thread >= identifier; | 505 current_thread >= identifier; |
| 385 | 506 |
| 386 BrowserThreadGlobals& globals = g_globals.Get(); | 507 BrowserThreadGlobals& globals = g_globals.Get(); |
| 387 if (!target_thread_outlives_current) | 508 if (!target_thread_outlives_current) |
| 388 globals.lock.Acquire(); | 509 globals.lock.Acquire(); |
| 389 | 510 |
| 390 base::MessageLoop* message_loop = | 511 const bool accepting_tasks = |
| 391 globals.threads[identifier] ? globals.threads[identifier]->message_loop() | 512 globals.states[identifier] == BrowserThreadState::RUNNING; |
| 392 : nullptr; | 513 if (accepting_tasks) { |
| 393 if (message_loop) { | 514 base::SingleThreadTaskRunner* task_runner = |
|
robliao
2016/12/08 01:57:17
Since we don't always lock here, it seems like it'
gab
2016/12/08 18:49:41
There is indeed a theoretical race as described in
| |
| 515 globals.task_runners[identifier].get(); | |
| 516 DCHECK(task_runner); | |
| 394 if (nestable) { | 517 if (nestable) { |
| 395 message_loop->task_runner()->PostDelayedTask(from_here, task, delay); | 518 task_runner->PostDelayedTask(from_here, task, delay); |
| 396 } else { | 519 } else { |
| 397 message_loop->task_runner()->PostNonNestableDelayedTask(from_here, task, | 520 task_runner->PostNonNestableDelayedTask(from_here, task, delay); |
| 398 delay); | |
| 399 } | 521 } |
| 400 } | 522 } |
| 401 | 523 |
| 402 if (!target_thread_outlives_current) | 524 if (!target_thread_outlives_current) |
| 403 globals.lock.Release(); | 525 globals.lock.Release(); |
| 404 | 526 |
| 405 return !!message_loop; | 527 return accepting_tasks; |
| 406 } | 528 } |
| 407 | 529 |
| 408 // static | 530 // static |
| 409 bool BrowserThread::PostBlockingPoolTask( | 531 bool BrowserThread::PostBlockingPoolTask( |
| 410 const tracked_objects::Location& from_here, | 532 const tracked_objects::Location& from_here, |
| 411 const base::Closure& task) { | 533 const base::Closure& task) { |
| 412 return g_globals.Get().blocking_pool->PostWorkerTask(from_here, task); | 534 return g_globals.Get().blocking_pool->PostWorkerTask(from_here, task); |
| 413 } | 535 } |
| 414 | 536 |
| 415 // static | 537 // static |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 446 | 568 |
| 447 // static | 569 // static |
| 448 bool BrowserThread::IsThreadInitialized(ID identifier) { | 570 bool BrowserThread::IsThreadInitialized(ID identifier) { |
| 449 if (g_globals == nullptr) | 571 if (g_globals == nullptr) |
| 450 return false; | 572 return false; |
| 451 | 573 |
| 452 BrowserThreadGlobals& globals = g_globals.Get(); | 574 BrowserThreadGlobals& globals = g_globals.Get(); |
| 453 base::AutoLock lock(globals.lock); | 575 base::AutoLock lock(globals.lock); |
| 454 DCHECK_GE(identifier, 0); | 576 DCHECK_GE(identifier, 0); |
| 455 DCHECK_LT(identifier, ID_COUNT); | 577 DCHECK_LT(identifier, ID_COUNT); |
| 456 return globals.threads[identifier] != nullptr; | 578 return globals.states[identifier] == BrowserThreadState::INITIALIZED || |
| 579 globals.states[identifier] == BrowserThreadState::RUNNING; | |
| 457 } | 580 } |
| 458 | 581 |
| 459 // static | 582 // static |
| 460 bool BrowserThread::CurrentlyOn(ID identifier) { | 583 bool BrowserThread::CurrentlyOn(ID identifier) { |
| 461 BrowserThreadGlobals& globals = g_globals.Get(); | 584 BrowserThreadGlobals& globals = g_globals.Get(); |
| 462 base::AutoLock lock(globals.lock); | 585 base::AutoLock lock(globals.lock); |
| 463 DCHECK_GE(identifier, 0); | 586 DCHECK_GE(identifier, 0); |
| 464 DCHECK_LT(identifier, ID_COUNT); | 587 DCHECK_LT(identifier, ID_COUNT); |
| 465 return base::PlatformThread::CurrentId() == globals.thread_ids[identifier]; | 588 return globals.task_runners[identifier] && |
| 589 globals.task_runners[identifier]->RunsTasksOnCurrentThread(); | |
| 466 } | 590 } |
| 467 | 591 |
| 468 // static | 592 // static |
| 469 std::string BrowserThread::GetDCheckCurrentlyOnErrorMessage(ID expected) { | 593 std::string BrowserThread::GetDCheckCurrentlyOnErrorMessage(ID expected) { |
| 470 std::string actual_name = base::PlatformThread::GetName(); | 594 std::string actual_name = base::PlatformThread::GetName(); |
| 471 if (actual_name.empty()) | 595 if (actual_name.empty()) |
| 472 actual_name = "Unknown Thread"; | 596 actual_name = "Unknown Thread"; |
| 473 | 597 |
| 474 std::string result = "Must be called on "; | 598 std::string result = "Must be called on "; |
| 475 result += GetThreadName(expected); | 599 result += GetThreadName(expected); |
| 476 result += "; actually called on "; | 600 result += "; actually called on "; |
| 477 result += actual_name; | 601 result += actual_name; |
| 478 result += "."; | 602 result += "."; |
| 479 return result; | 603 return result; |
| 480 } | 604 } |
| 481 | 605 |
| 482 // static | 606 // static |
| 483 bool BrowserThread::IsMessageLoopValid(ID identifier) { | 607 bool BrowserThread::IsMessageLoopValid(ID identifier) { |
| 484 if (g_globals == nullptr) | 608 if (g_globals == nullptr) |
| 485 return false; | 609 return false; |
| 486 | 610 |
| 487 BrowserThreadGlobals& globals = g_globals.Get(); | 611 BrowserThreadGlobals& globals = g_globals.Get(); |
| 488 base::AutoLock lock(globals.lock); | 612 base::AutoLock lock(globals.lock); |
| 489 DCHECK_GE(identifier, 0); | 613 DCHECK_GE(identifier, 0); |
| 490 DCHECK_LT(identifier, ID_COUNT); | 614 DCHECK_LT(identifier, ID_COUNT); |
| 491 return globals.threads[identifier] && | 615 return globals.states[identifier] == BrowserThreadState::RUNNING; |
| 492 globals.threads[identifier]->message_loop(); | |
| 493 } | 616 } |
| 494 | 617 |
| 495 // static | 618 // static |
| 496 bool BrowserThread::PostTask(ID identifier, | 619 bool BrowserThread::PostTask(ID identifier, |
| 497 const tracked_objects::Location& from_here, | 620 const tracked_objects::Location& from_here, |
| 498 const base::Closure& task) { | 621 const base::Closure& task) { |
| 499 return BrowserThreadImpl::PostTaskHelper( | 622 return BrowserThreadImpl::PostTaskHelper( |
| 500 identifier, from_here, task, base::TimeDelta(), true); | 623 identifier, from_here, task, base::TimeDelta(), true); |
| 501 } | 624 } |
| 502 | 625 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 536 const base::Closure& reply) { | 659 const base::Closure& reply) { |
| 537 return GetTaskRunnerForThread(identifier) | 660 return GetTaskRunnerForThread(identifier) |
| 538 ->PostTaskAndReply(from_here, task, reply); | 661 ->PostTaskAndReply(from_here, task, reply); |
| 539 } | 662 } |
| 540 | 663 |
| 541 // static | 664 // static |
| 542 bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) { | 665 bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) { |
| 543 if (g_globals == nullptr) | 666 if (g_globals == nullptr) |
| 544 return false; | 667 return false; |
| 545 | 668 |
| 546 base::MessageLoop* cur_message_loop = base::MessageLoop::current(); | |
| 547 BrowserThreadGlobals& globals = g_globals.Get(); | 669 BrowserThreadGlobals& globals = g_globals.Get(); |
| 548 // Profiler to track potential contention on |globals.lock|. This only does | 670 // Profiler to track potential contention on |globals.lock|. This only does |
| 549 // real work on canary and local dev builds, so the cost of having this here | 671 // real work on canary and local dev builds, so the cost of having this here |
| 550 // should be minimal. | 672 // should be minimal. |
| 551 tracked_objects::ScopedTracker tracking_profile(FROM_HERE); | 673 tracked_objects::ScopedTracker tracking_profile(FROM_HERE); |
| 552 base::AutoLock lock(globals.lock); | 674 base::AutoLock lock(globals.lock); |
| 553 for (int i = 0; i < ID_COUNT; ++i) { | 675 for (int i = 0; i < ID_COUNT; ++i) { |
| 554 if (globals.threads[i] && | 676 if (globals.task_runners[i] && |
| 555 globals.threads[i]->message_loop() == cur_message_loop) { | 677 globals.task_runners[i]->RunsTasksOnCurrentThread()) { |
| 556 *identifier = globals.threads[i]->identifier_; | 678 *identifier = static_cast<ID>(i); |
| 557 return true; | 679 return true; |
| 558 } | 680 } |
| 559 } | 681 } |
| 560 | 682 |
| 561 return false; | 683 return false; |
| 562 } | 684 } |
| 563 | 685 |
| 564 // static | 686 // static |
| 565 scoped_refptr<base::SingleThreadTaskRunner> | 687 scoped_refptr<base::SingleThreadTaskRunner> |
| 566 BrowserThread::GetTaskRunnerForThread(ID identifier) { | 688 BrowserThread::GetTaskRunnerForThread(ID identifier) { |
| 567 return g_task_runners.Get().proxies[identifier]; | 689 return g_task_runners.Get().proxies[identifier]; |
| 568 } | 690 } |
| 569 | 691 |
| 570 // static | 692 // static |
| 571 void BrowserThread::SetIOThreadDelegate(BrowserThreadDelegate* delegate) { | 693 void BrowserThread::SetIOThreadDelegate(BrowserThreadDelegate* delegate) { |
| 572 BrowserThreadGlobals& globals = g_globals.Get(); | 694 BrowserThreadGlobals& globals = g_globals.Get(); |
| 573 BrowserThreadDelegateAtomicPtr old_delegate = | 695 BrowserThreadDelegateAtomicPtr old_delegate = |
| 574 base::subtle::NoBarrier_AtomicExchange( | 696 base::subtle::NoBarrier_AtomicExchange( |
| 575 &globals.io_thread_delegate, | 697 &globals.io_thread_delegate, |
| 576 reinterpret_cast<BrowserThreadDelegateAtomicPtr>(delegate)); | 698 reinterpret_cast<BrowserThreadDelegateAtomicPtr>(delegate)); |
| 577 | 699 |
| 578 // This catches registration when previously registered. | 700 // This catches registration when previously registered. |
| 579 DCHECK(!delegate || !old_delegate); | 701 DCHECK(!delegate || !old_delegate); |
| 580 } | 702 } |
| 581 | 703 |
| 582 } // namespace content | 704 } // namespace content |
| OLD | NEW |