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/task_scheduler/task_scheduler.h" |
|
fdoray
2016/12/01 15:15:21
Not needed.
gab
2016/12/07 19:15:27
Done.
| |
| 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 but the underlying thread hasn't run yet. | |
|
fdoray
2016/12/01 15:15:22
The INITIALIZED state is useless. See comment in B
gab
2016/12/07 19:15:27
It is useful, replied there.
| |
| 110 INITIALIZED, | |
| 111 // BrowserThread::ID is associated to a live thread. | |
|
fdoray
2016/12/01 15:15:22
... is associated to TaskRunner and is accepting t
gab
2016/12/07 19:15:27
Done.
| |
| 112 RUNNING, | |
| 113 // BrowserThread::ID is associated to a thread having been or being shutdown. | |
|
fdoray
2016/12/01 15:15:21
... no longer accepts tasks. If it was backed by a
gab
2016/12/07 19:15:27
Done (without second sentence).
| |
| 114 SHUTDOWN | |
| 115 }; | |
| 116 | |
| 102 struct BrowserThreadGlobals { | 117 struct BrowserThreadGlobals { |
| 103 BrowserThreadGlobals() | 118 BrowserThreadGlobals() |
| 104 : blocking_pool( | 119 : blocking_pool( |
| 105 new base::SequencedWorkerPool(3, | 120 new base::SequencedWorkerPool(3, |
| 106 "BrowserBlocking", | 121 "BrowserBlocking", |
| 107 base::TaskPriority::USER_VISIBLE)) { | 122 base::TaskPriority::USER_VISIBLE)) {} |
| 108 memset(threads, 0, BrowserThread::ID_COUNT * sizeof(threads[0])); | |
| 109 memset(thread_ids, 0, BrowserThread::ID_COUNT * sizeof(thread_ids[0])); | |
| 110 memset(thread_delegates, 0, | |
| 111 BrowserThread::ID_COUNT * sizeof(thread_delegates[0])); | |
| 112 } | |
| 113 | 123 |
| 114 // This lock protects |threads| and |thread_ids|. Do not read or modify those | 124 // 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. | 125 // arrays without holding this lock. Do not block while holding this lock. |
| 116 base::Lock lock; | 126 base::Lock lock; |
| 117 | 127 |
| 118 // This array is protected by |lock|. IDs in this array are populated as soon | 128 // This array is filled either as the underlying threads start and invoke |
| 119 // as their respective thread is started and are never reset. | 129 // Init() or in RedirectThreadIDToTaskRunner() for threads that are being |
| 120 base::PlatformThreadId thread_ids[BrowserThread::ID_COUNT]; | 130 // redirected. It is not emptied during shutdown in order to support |
| 131 // RunsTasksOnCurrentThread() until the very end. | |
| 132 scoped_refptr<base::SingleThreadTaskRunner> | |
| 133 task_runners[BrowserThread::ID_COUNT]; | |
| 121 | 134 |
| 122 // This array is protected by |lock|. The threads are not owned by this | 135 // Holds the state of each BrowserThread::ID. This needs to be a separate bit |
| 123 // array. Typically, the threads are owned on the UI thread by | 136 // because |threads| isn't a good signal per redirected IDs remaining null and |
|
fdoray
2016/12/01 15:15:22
|threads| doesn't exist
gab
2016/12/07 19:15:27
Done.
| |
| 124 // BrowserMainLoop. BrowserThreadImpl objects remove themselves from this | 137 // |task_runners| isn't either per not being cleaned up on shutdown. |
| 125 // array upon destruction. | 138 BrowserThreadState states[BrowserThread::ID_COUNT] = {}; |
| 126 BrowserThreadImpl* threads[BrowserThread::ID_COUNT]; | |
| 127 | 139 |
| 128 // Only atomic operations are used on this array. The delegates are not owned | 140 // Only atomic operations are used on this array. The delegates are not owned |
| 129 // by this array, rather by whoever calls BrowserThread::SetDelegate. | 141 // by this array, rather by whoever calls BrowserThread::SetDelegate. |
| 130 BrowserThreadDelegate* thread_delegates[BrowserThread::ID_COUNT]; | 142 BrowserThreadDelegate* thread_delegates[BrowserThread::ID_COUNT] = {}; |
| 131 | 143 |
| 132 const scoped_refptr<base::SequencedWorkerPool> blocking_pool; | 144 const scoped_refptr<base::SequencedWorkerPool> blocking_pool; |
| 133 }; | 145 }; |
| 134 | 146 |
| 135 base::LazyInstance<BrowserThreadGlobals>::Leaky | 147 base::LazyInstance<BrowserThreadGlobals>::Leaky |
| 136 g_globals = LAZY_INSTANCE_INITIALIZER; | 148 g_globals = LAZY_INSTANCE_INITIALIZER; |
| 137 | 149 |
| 138 } // namespace | 150 } // namespace |
| 139 | 151 |
| 140 BrowserThreadImpl::BrowserThreadImpl(ID identifier) | 152 BrowserThreadImpl::BrowserThreadImpl(ID identifier) |
| 141 : Thread(GetThreadName(identifier)), identifier_(identifier) { | 153 : Thread(GetThreadName(identifier)), identifier_(identifier) { |
| 142 Initialize(); | 154 Initialize(); |
| 143 | |
| 144 // Unit tests may create multiple TestBrowserThreadBundles, causing the same | |
| 145 // BrowserThread ID to be reinitialized. We explicitly clear the thread ID | |
| 146 // here so Start() can sanity check. | |
| 147 BrowserThreadGlobals& globals = g_globals.Get(); | |
| 148 base::AutoLock lock(globals.lock); | |
| 149 globals.thread_ids[identifier] = base::kInvalidThreadId; | |
| 150 } | 155 } |
| 151 | 156 |
| 152 BrowserThreadImpl::BrowserThreadImpl(ID identifier, | 157 BrowserThreadImpl::BrowserThreadImpl(ID identifier, |
| 153 base::MessageLoop* message_loop) | 158 base::MessageLoop* message_loop) |
| 154 : Thread(GetThreadName(identifier)), identifier_(identifier) { | 159 : Thread(GetThreadName(identifier)), identifier_(identifier) { |
| 155 SetMessageLoop(message_loop); | 160 SetMessageLoop(message_loop); |
| 156 Initialize(); | 161 Initialize(); |
| 157 | 162 |
| 158 // If constructed with an explicit message loop, this is a fake BrowserThread | 163 // If constructed with an explicit message loop, this is a fake |
| 159 // which runs on the current thread. | 164 // BrowserThread which runs on the current thread. |
| 160 BrowserThreadGlobals& globals = g_globals.Get(); | 165 BrowserThreadGlobals& globals = g_globals.Get(); |
| 161 base::AutoLock lock(globals.lock); | 166 base::AutoLock lock(globals.lock); |
| 162 globals.thread_ids[identifier] = base::PlatformThread::CurrentId(); | 167 |
| 168 DCHECK(!globals.task_runners[identifier_]); | |
| 169 globals.task_runners[identifier_] = task_runner(); | |
| 170 | |
| 171 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::INITIALIZED); | |
| 172 globals.states[identifier_] = BrowserThreadState::RUNNING; | |
| 163 } | 173 } |
| 164 | 174 |
| 165 // static | 175 // static |
| 166 void BrowserThreadImpl::ShutdownThreadPool() { | 176 void BrowserThreadImpl::ShutdownThreadPool() { |
| 167 // The goal is to make it impossible for chrome to 'infinite loop' during | 177 // The goal is to make it impossible for chrome to 'infinite loop' during |
| 168 // shutdown, but to reasonably expect that all BLOCKING_SHUTDOWN tasks queued | 178 // shutdown, but to reasonably expect that all BLOCKING_SHUTDOWN tasks queued |
| 169 // during shutdown get run. There's nothing particularly scientific about the | 179 // during shutdown get run. There's nothing particularly scientific about the |
| 170 // number chosen. | 180 // number chosen. |
| 171 const int kMaxNewShutdownBlockingTasks = 1000; | 181 const int kMaxNewShutdownBlockingTasks = 1000; |
| 172 BrowserThreadGlobals& globals = g_globals.Get(); | 182 BrowserThreadGlobals& globals = g_globals.Get(); |
| 173 globals.blocking_pool->Shutdown(kMaxNewShutdownBlockingTasks); | 183 globals.blocking_pool->Shutdown(kMaxNewShutdownBlockingTasks); |
| 174 } | 184 } |
| 175 | 185 |
| 176 // static | 186 // static |
| 177 void BrowserThreadImpl::FlushThreadPoolHelperForTesting() { | 187 void BrowserThreadImpl::FlushThreadPoolHelperForTesting() { |
| 178 // We don't want to create a pool if none exists. | 188 // We don't want to create a pool if none exists. |
| 179 if (g_globals == nullptr) | 189 if (g_globals == nullptr) |
| 180 return; | 190 return; |
| 181 g_globals.Get().blocking_pool->FlushForTesting(); | 191 g_globals.Get().blocking_pool->FlushForTesting(); |
| 182 disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting(); | 192 disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting(); |
| 183 } | 193 } |
| 184 | 194 |
| 185 void BrowserThreadImpl::Init() { | 195 void BrowserThreadImpl::Init() { |
| 186 BrowserThreadGlobals& globals = g_globals.Get(); | 196 BrowserThreadGlobals& globals = g_globals.Get(); |
| 187 | 197 |
| 198 // |globals| should already have been initialized for |identifier_| in | |
| 199 // BrowserThreadImpl::StartWithOptions(). If this isn't the case it's likely | |
| 200 // because this BrowserThreadImpl's owner incorrectly used Thread::Start.*() | |
| 201 // instead of BrowserThreadImpl::Start.*(). | |
| 202 DCHECK(globals.task_runners[identifier_]); | |
| 203 DCHECK(globals.task_runners[identifier_]->RunsTasksOnCurrentThread()); | |
| 204 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::RUNNING); | |
| 205 | |
| 188 if (BrowserThread::CurrentlyOn(BrowserThread::DB) || | 206 if (BrowserThread::CurrentlyOn(BrowserThread::DB) || |
| 189 BrowserThread::CurrentlyOn(BrowserThread::FILE) || | 207 BrowserThread::CurrentlyOn(BrowserThread::FILE) || |
| 190 BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING) || | 208 BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING) || |
| 191 BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER) || | 209 BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER) || |
| 192 BrowserThread::CurrentlyOn(BrowserThread::CACHE)) { | 210 BrowserThread::CurrentlyOn(BrowserThread::CACHE)) { |
| 193 base::MessageLoop* message_loop = base::MessageLoop::current(); | 211 // Nesting and task observers are not allowed on redirected threads. |
| 194 message_loop->DisallowNesting(); | 212 message_loop()->DisallowNesting(); |
| 195 message_loop->DisallowTaskObservers(); | 213 message_loop()->DisallowTaskObservers(); |
| 196 } | 214 } |
| 197 | 215 |
| 198 using base::subtle::AtomicWord; | 216 using base::subtle::AtomicWord; |
| 199 AtomicWord* storage = | 217 AtomicWord* storage = |
| 200 reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]); | 218 reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]); |
| 201 AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage); | 219 AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage); |
| 202 BrowserThreadDelegate* delegate = | 220 BrowserThreadDelegate* delegate = |
| 203 reinterpret_cast<BrowserThreadDelegate*>(stored_pointer); | 221 reinterpret_cast<BrowserThreadDelegate*>(stored_pointer); |
| 204 if (delegate) | 222 if (delegate) |
| 205 delegate->Init(); | 223 delegate->Init(); |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 302 using base::subtle::AtomicWord; | 320 using base::subtle::AtomicWord; |
| 303 AtomicWord* storage = | 321 AtomicWord* storage = |
| 304 reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]); | 322 reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]); |
| 305 AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage); | 323 AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage); |
| 306 BrowserThreadDelegate* delegate = | 324 BrowserThreadDelegate* delegate = |
| 307 reinterpret_cast<BrowserThreadDelegate*>(stored_pointer); | 325 reinterpret_cast<BrowserThreadDelegate*>(stored_pointer); |
| 308 | 326 |
| 309 if (delegate) | 327 if (delegate) |
| 310 delegate->CleanUp(); | 328 delegate->CleanUp(); |
| 311 | 329 |
| 312 // PostTaskHelper() accesses the message loop while holding this lock. | 330 // PostTaskHelper() accesses the message loop while holding this lock. |
|
fdoray
2016/12/01 15:15:22
PostTaskHelper() no longer accesses a MessageLoop.
gab
2016/12/07 19:15:27
Done (slightly reworded).
| |
| 313 // However, the message loop will soon be destructed without any locking. So | 331 // However, the message loop will soon be destructed without any locking. So |
| 314 // to prevent a race with accessing the message loop in PostTaskHelper(), | 332 // to prevent a race with accessing the message loop in PostTaskHelper(), |
| 315 // remove this thread from the global array now. | 333 // remove this thread from the global array now. |
| 316 base::AutoLock lock(globals.lock); | 334 base::AutoLock lock(globals.lock); |
| 317 globals.threads[identifier_] = nullptr; | 335 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::RUNNING); |
| 336 globals.states[identifier_] = BrowserThreadState::SHUTDOWN; | |
| 318 } | 337 } |
| 319 | 338 |
| 320 void BrowserThreadImpl::Initialize() { | 339 void BrowserThreadImpl::Initialize() { |
| 321 BrowserThreadGlobals& globals = g_globals.Get(); | 340 BrowserThreadGlobals& globals = g_globals.Get(); |
| 322 | 341 |
| 323 base::AutoLock lock(globals.lock); | 342 base::AutoLock lock(globals.lock); |
| 324 DCHECK_GE(identifier_, 0); | 343 DCHECK_GE(identifier_, 0); |
| 325 DCHECK_LT(identifier_, ID_COUNT); | 344 DCHECK_LT(identifier_, ID_COUNT); |
| 326 DCHECK_EQ(globals.threads[identifier_], nullptr); | 345 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::UNINITIALIZED); |
| 327 globals.threads[identifier_] = this; | 346 globals.states[identifier_] = BrowserThreadState::INITIALIZED; |
|
fdoray
2016/12/01 15:15:21
You can get rid of the INITIALIZED state. Initiali
gab
2016/12/07 19:15:27
No, INITIALIZED is required to support BrowserThre
| |
| 347 } | |
| 348 | |
| 349 // static | |
| 350 void BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::ID identifier) { | |
| 351 BrowserThreadGlobals& globals = g_globals.Get(); | |
| 352 | |
| 353 base::AutoLock lock(globals.lock); | |
| 354 DCHECK_EQ(globals.states[identifier], BrowserThreadState::SHUTDOWN); | |
| 355 globals.states[identifier] = BrowserThreadState::UNINITIALIZED; | |
| 356 globals.task_runners[identifier] = nullptr; | |
| 357 SetDelegate(identifier, nullptr); | |
| 328 } | 358 } |
| 329 | 359 |
| 330 BrowserThreadImpl::~BrowserThreadImpl() { | 360 BrowserThreadImpl::~BrowserThreadImpl() { |
| 331 // All Thread subclasses must call Stop() in the destructor. This is | 361 // All Thread subclasses must call Stop() in the destructor. This is |
| 332 // doubly important here as various bits of code check they are on | 362 // doubly important here as various bits of code check they are on |
| 333 // the right BrowserThread. | 363 // the right BrowserThread. |
| 334 Stop(); | 364 Stop(); |
| 335 | 365 |
| 336 BrowserThreadGlobals& globals = g_globals.Get(); | 366 BrowserThreadGlobals& globals = g_globals.Get(); |
| 337 base::AutoLock lock(globals.lock); | 367 base::AutoLock lock(globals.lock); |
| 338 globals.threads[identifier_] = nullptr; | 368 // This thread should have gone through Cleanup() as part of Stop() and be in |
| 339 #ifndef NDEBUG | 369 // the SHUTDOWN state already (unless it uses an externally provided |
| 370 // MessageLoop instead of a real underlying thread and thus doesn't go through | |
| 371 // Cleanup()). | |
| 372 if (using_external_message_loop()) { | |
| 373 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::RUNNING); | |
| 374 globals.states[identifier_] = BrowserThreadState::SHUTDOWN; | |
| 375 } else { | |
| 376 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::SHUTDOWN); | |
| 377 } | |
| 378 #if DCHECK_IS_ON() | |
| 340 // Double check that the threads are ordered correctly in the enumeration. | 379 // Double check that the threads are ordered correctly in the enumeration. |
| 341 for (int i = identifier_ + 1; i < ID_COUNT; ++i) { | 380 for (int i = identifier_ + 1; i < ID_COUNT; ++i) { |
| 342 DCHECK(!globals.threads[i]) << | 381 DCHECK(globals.states[i] == BrowserThreadState::SHUTDOWN || |
| 343 "Threads must be listed in the reverse order that they die"; | 382 globals.states[i] == BrowserThreadState::UNINITIALIZED) |
| 383 << "Threads must be listed in the reverse order that they die"; | |
| 344 } | 384 } |
| 345 #endif | 385 #endif |
| 346 } | 386 } |
| 347 | 387 |
| 348 bool BrowserThreadImpl::Start() { | 388 bool BrowserThreadImpl::Start() { |
| 349 return StartWithOptions(base::Thread::Options()); | 389 return StartWithOptions(base::Thread::Options()); |
| 350 } | 390 } |
| 351 | 391 |
| 352 bool BrowserThreadImpl::StartWithOptions(const Options& options) { | 392 bool BrowserThreadImpl::StartWithOptions(const Options& options) { |
| 353 // The global thread table needs to be locked while a new thread is | 393 // The lock doesn't need to be held to start the thread. The thread will |
| 354 // starting, as the new thread can asynchronously start touching the | 394 // start asynchronously and eventually invoke BrowserThreadImpl::Init() |
| 355 // table (and other thread's message_loop). | 395 // to complete initialization. |
| 396 bool result = Thread::StartWithOptions(options); | |
| 397 | |
| 356 BrowserThreadGlobals& globals = g_globals.Get(); | 398 BrowserThreadGlobals& globals = g_globals.Get(); |
| 357 base::AutoLock lock(globals.lock); | 399 base::AutoLock lock(globals.lock); |
| 358 DCHECK_EQ(globals.thread_ids[identifier_], base::kInvalidThreadId); | 400 |
| 359 bool result = Thread::StartWithOptions(options); | 401 // Although the the thread is starting asynchronously. The MessageLoop is |
| 360 globals.thread_ids[identifier_] = GetThreadId(); | 402 // already ready to accept tasks and as such this BrowserThreadImpl is |
| 403 // considered as "running". | |
| 404 DCHECK(!globals.task_runners[identifier_]); | |
| 405 globals.task_runners[identifier_] = task_runner(); | |
| 406 DCHECK(globals.task_runners[identifier_]); | |
| 407 | |
| 408 DCHECK_EQ(globals.states[identifier_], BrowserThreadState::INITIALIZED); | |
| 409 globals.states[identifier_] = BrowserThreadState::RUNNING; | |
| 410 | |
| 361 return result; | 411 return result; |
| 362 } | 412 } |
| 363 | 413 |
| 364 bool BrowserThreadImpl::StartAndWaitForTesting() { | 414 bool BrowserThreadImpl::StartAndWaitForTesting() { |
| 365 if (!Start()) | 415 if (!Start()) |
| 366 return false; | 416 return false; |
| 367 WaitUntilThreadStarted(); | 417 WaitUntilThreadStarted(); |
| 368 return true; | 418 return true; |
| 369 } | 419 } |
| 420 | |
| 421 // static | |
| 422 void BrowserThreadImpl::RedirectThreadIDToTaskRunner( | |
| 423 BrowserThread::ID identifier, | |
| 424 scoped_refptr<base::SingleThreadTaskRunner> task_runner) { | |
| 425 BrowserThreadGlobals& globals = g_globals.Get(); | |
| 426 base::AutoLock lock(globals.lock); | |
| 427 if (task_runner) { | |
| 428 DCHECK(!globals.task_runners[identifier]); | |
| 429 DCHECK_EQ(globals.states[identifier], BrowserThreadState::UNINITIALIZED); | |
| 430 | |
| 431 globals.task_runners[identifier] = std::move(task_runner); | |
| 432 globals.states[identifier] = BrowserThreadState::RUNNING; | |
| 433 } else { | |
| 434 // Cancelling redirection should only be done on previously redirected | |
| 435 // threads. | |
|
fdoray
2016/12/01 15:15:22
Add:
// Change the state to SHUTDOWN so that Post
gab
2016/12/07 19:15:27
Detailed comments added to BrowserThreadImpl::Stop
| |
| 436 DCHECK_EQ(globals.states[identifier], BrowserThreadState::RUNNING); | |
| 437 globals.states[identifier] = BrowserThreadState::SHUTDOWN; | |
| 438 } | |
| 439 } | |
| 440 | |
| 370 // static | 441 // static |
| 371 bool BrowserThreadImpl::PostTaskHelper( | 442 bool BrowserThreadImpl::PostTaskHelper( |
| 372 BrowserThread::ID identifier, | 443 BrowserThread::ID identifier, |
| 373 const tracked_objects::Location& from_here, | 444 const tracked_objects::Location& from_here, |
| 374 const base::Closure& task, | 445 const base::Closure& task, |
| 375 base::TimeDelta delay, | 446 base::TimeDelta delay, |
| 376 bool nestable) { | 447 bool nestable) { |
| 377 DCHECK_GE(identifier, 0); | 448 DCHECK_GE(identifier, 0); |
| 378 DCHECK_LT(identifier, ID_COUNT); | 449 DCHECK_LT(identifier, ID_COUNT); |
| 379 // Optimization: to avoid unnecessary locks, we listed the ID enumeration in | 450 // Optimization: to avoid unnecessary locks, we listed the ID enumeration in |
| 380 // order of lifetime. So no need to lock if we know that the target thread | 451 // order of lifetime. So no need to lock if we know that the target thread |
| 381 // outlives current thread. | 452 // outlives current thread. |
| 382 // Note: since the array is so small, ok to loop instead of creating a map, | 453 // Note: since the array is so small, ok to loop instead of creating a map, |
| 383 // which would require a lock because std::map isn't thread safe, defeating | 454 // which would require a lock because std::map isn't thread safe, defeating |
| 384 // the whole purpose of this optimization. | 455 // the whole purpose of this optimization. |
| 385 BrowserThread::ID current_thread = ID_COUNT; | 456 BrowserThread::ID current_thread = ID_COUNT; |
| 386 bool target_thread_outlives_current = | 457 bool target_thread_outlives_current = |
| 387 GetCurrentThreadIdentifier(¤t_thread) && | 458 GetCurrentThreadIdentifier(¤t_thread) && |
| 388 current_thread >= identifier; | 459 current_thread >= identifier; |
| 389 | 460 |
| 390 BrowserThreadGlobals& globals = g_globals.Get(); | 461 BrowserThreadGlobals& globals = g_globals.Get(); |
| 391 if (!target_thread_outlives_current) | 462 if (!target_thread_outlives_current) |
| 392 globals.lock.Acquire(); | 463 globals.lock.Acquire(); |
| 393 | 464 |
| 394 base::MessageLoop* message_loop = | 465 const bool accepting_tasks = |
| 395 globals.threads[identifier] ? globals.threads[identifier]->message_loop() | 466 globals.states[identifier] == BrowserThreadState::RUNNING; |
| 396 : nullptr; | 467 if (accepting_tasks) { |
| 397 if (message_loop) { | 468 base::SingleThreadTaskRunner* task_runner = |
| 469 globals.task_runners[identifier].get(); | |
| 470 DCHECK(task_runner); | |
| 398 if (nestable) { | 471 if (nestable) { |
| 399 message_loop->task_runner()->PostDelayedTask(from_here, task, delay); | 472 task_runner->PostDelayedTask(from_here, task, delay); |
| 400 } else { | 473 } else { |
| 401 message_loop->task_runner()->PostNonNestableDelayedTask(from_here, task, | 474 task_runner->PostNonNestableDelayedTask(from_here, task, delay); |
| 402 delay); | |
| 403 } | 475 } |
| 404 } | 476 } |
| 405 | 477 |
| 406 if (!target_thread_outlives_current) | 478 if (!target_thread_outlives_current) |
| 407 globals.lock.Release(); | 479 globals.lock.Release(); |
| 408 | 480 |
| 409 return !!message_loop; | 481 return accepting_tasks; |
| 410 } | 482 } |
| 411 | 483 |
| 412 // static | 484 // static |
| 413 bool BrowserThread::PostBlockingPoolTask( | 485 bool BrowserThread::PostBlockingPoolTask( |
| 414 const tracked_objects::Location& from_here, | 486 const tracked_objects::Location& from_here, |
| 415 const base::Closure& task) { | 487 const base::Closure& task) { |
| 416 return g_globals.Get().blocking_pool->PostWorkerTask(from_here, task); | 488 return g_globals.Get().blocking_pool->PostWorkerTask(from_here, task); |
| 417 } | 489 } |
| 418 | 490 |
| 419 // static | 491 // static |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 450 | 522 |
| 451 // static | 523 // static |
| 452 bool BrowserThread::IsThreadInitialized(ID identifier) { | 524 bool BrowserThread::IsThreadInitialized(ID identifier) { |
| 453 if (g_globals == nullptr) | 525 if (g_globals == nullptr) |
| 454 return false; | 526 return false; |
| 455 | 527 |
| 456 BrowserThreadGlobals& globals = g_globals.Get(); | 528 BrowserThreadGlobals& globals = g_globals.Get(); |
| 457 base::AutoLock lock(globals.lock); | 529 base::AutoLock lock(globals.lock); |
| 458 DCHECK_GE(identifier, 0); | 530 DCHECK_GE(identifier, 0); |
| 459 DCHECK_LT(identifier, ID_COUNT); | 531 DCHECK_LT(identifier, ID_COUNT); |
| 460 return globals.threads[identifier] != nullptr; | 532 return globals.states[identifier] == BrowserThreadState::INITIALIZED || |
| 533 globals.states[identifier] == BrowserThreadState::RUNNING; | |
| 461 } | 534 } |
| 462 | 535 |
| 463 // static | 536 // static |
| 464 bool BrowserThread::CurrentlyOn(ID identifier) { | 537 bool BrowserThread::CurrentlyOn(ID identifier) { |
| 465 BrowserThreadGlobals& globals = g_globals.Get(); | 538 BrowserThreadGlobals& globals = g_globals.Get(); |
| 466 base::AutoLock lock(globals.lock); | 539 base::AutoLock lock(globals.lock); |
| 467 DCHECK_GE(identifier, 0); | 540 DCHECK_GE(identifier, 0); |
| 468 DCHECK_LT(identifier, ID_COUNT); | 541 DCHECK_LT(identifier, ID_COUNT); |
| 469 return base::PlatformThread::CurrentId() == globals.thread_ids[identifier]; | 542 return globals.task_runners[identifier] && |
| 543 globals.task_runners[identifier]->RunsTasksOnCurrentThread(); | |
| 470 } | 544 } |
| 471 | 545 |
| 472 // static | 546 // static |
| 473 std::string BrowserThread::GetDCheckCurrentlyOnErrorMessage(ID expected) { | 547 std::string BrowserThread::GetDCheckCurrentlyOnErrorMessage(ID expected) { |
| 474 std::string actual_name = base::PlatformThread::GetName(); | 548 std::string actual_name = base::PlatformThread::GetName(); |
| 475 if (actual_name.empty()) | 549 if (actual_name.empty()) |
| 476 actual_name = "Unknown Thread"; | 550 actual_name = "Unknown Thread"; |
| 477 | 551 |
| 478 std::string result = "Must be called on "; | 552 std::string result = "Must be called on "; |
| 479 result += GetThreadName(expected); | 553 result += GetThreadName(expected); |
| 480 result += "; actually called on "; | 554 result += "; actually called on "; |
| 481 result += actual_name; | 555 result += actual_name; |
| 482 result += "."; | 556 result += "."; |
| 483 return result; | 557 return result; |
| 484 } | 558 } |
| 485 | 559 |
| 486 // static | 560 // static |
| 487 bool BrowserThread::IsMessageLoopValid(ID identifier) { | 561 bool BrowserThread::IsMessageLoopValid(ID identifier) { |
| 488 if (g_globals == nullptr) | 562 if (g_globals == nullptr) |
| 489 return false; | 563 return false; |
| 490 | 564 |
| 491 BrowserThreadGlobals& globals = g_globals.Get(); | 565 BrowserThreadGlobals& globals = g_globals.Get(); |
| 492 base::AutoLock lock(globals.lock); | 566 base::AutoLock lock(globals.lock); |
| 493 DCHECK_GE(identifier, 0); | 567 DCHECK_GE(identifier, 0); |
| 494 DCHECK_LT(identifier, ID_COUNT); | 568 DCHECK_LT(identifier, ID_COUNT); |
| 495 return globals.threads[identifier] && | 569 return globals.states[identifier] == BrowserThreadState::RUNNING; |
| 496 globals.threads[identifier]->message_loop(); | |
| 497 } | 570 } |
| 498 | 571 |
| 499 // static | 572 // static |
| 500 bool BrowserThread::PostTask(ID identifier, | 573 bool BrowserThread::PostTask(ID identifier, |
| 501 const tracked_objects::Location& from_here, | 574 const tracked_objects::Location& from_here, |
| 502 const base::Closure& task) { | 575 const base::Closure& task) { |
| 503 return BrowserThreadImpl::PostTaskHelper( | 576 return BrowserThreadImpl::PostTaskHelper( |
| 504 identifier, from_here, task, base::TimeDelta(), true); | 577 identifier, from_here, task, base::TimeDelta(), true); |
| 505 } | 578 } |
| 506 | 579 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 540 const base::Closure& reply) { | 613 const base::Closure& reply) { |
| 541 return GetTaskRunnerForThread(identifier) | 614 return GetTaskRunnerForThread(identifier) |
| 542 ->PostTaskAndReply(from_here, task, reply); | 615 ->PostTaskAndReply(from_here, task, reply); |
| 543 } | 616 } |
| 544 | 617 |
| 545 // static | 618 // static |
| 546 bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) { | 619 bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) { |
| 547 if (g_globals == nullptr) | 620 if (g_globals == nullptr) |
| 548 return false; | 621 return false; |
| 549 | 622 |
| 550 base::MessageLoop* cur_message_loop = base::MessageLoop::current(); | |
| 551 BrowserThreadGlobals& globals = g_globals.Get(); | 623 BrowserThreadGlobals& globals = g_globals.Get(); |
| 552 // Profiler to track potential contention on |globals.lock|. This only does | 624 // Profiler to track potential contention on |globals.lock|. This only does |
| 553 // real work on canary and local dev builds, so the cost of having this here | 625 // real work on canary and local dev builds, so the cost of having this here |
| 554 // should be minimal. | 626 // should be minimal. |
| 555 tracked_objects::ScopedTracker tracking_profile(FROM_HERE); | 627 tracked_objects::ScopedTracker tracking_profile(FROM_HERE); |
| 556 base::AutoLock lock(globals.lock); | 628 base::AutoLock lock(globals.lock); |
| 557 for (int i = 0; i < ID_COUNT; ++i) { | 629 for (int i = 0; i < ID_COUNT; ++i) { |
| 558 if (globals.threads[i] && | 630 if (globals.task_runners[i] && |
| 559 globals.threads[i]->message_loop() == cur_message_loop) { | 631 globals.task_runners[i]->RunsTasksOnCurrentThread()) { |
| 560 *identifier = globals.threads[i]->identifier_; | 632 *identifier = static_cast<ID>(i); |
| 561 return true; | 633 return true; |
| 562 } | 634 } |
| 563 } | 635 } |
| 564 | 636 |
| 565 return false; | 637 return false; |
| 566 } | 638 } |
| 567 | 639 |
| 568 // static | 640 // static |
| 569 scoped_refptr<base::SingleThreadTaskRunner> | 641 scoped_refptr<base::SingleThreadTaskRunner> |
| 570 BrowserThread::GetTaskRunnerForThread(ID identifier) { | 642 BrowserThread::GetTaskRunnerForThread(ID identifier) { |
| 571 return g_task_runners.Get().proxies[identifier]; | 643 return g_task_runners.Get().proxies[identifier]; |
| 572 } | 644 } |
| 573 | 645 |
| 646 // static | |
| 574 void BrowserThread::SetDelegate(ID identifier, | 647 void BrowserThread::SetDelegate(ID identifier, |
|
fdoray
2016/12/01 15:15:22
I would change this to SetIOThreadDelegate() and c
gab
2016/12/07 19:15:27
Good call, will do in precursor CL.
gab
2016/12/08 18:49:41
Done in http://crrev.com/437271 :)
| |
| 575 BrowserThreadDelegate* delegate) { | 648 BrowserThreadDelegate* delegate) { |
| 576 using base::subtle::AtomicWord; | 649 using base::subtle::AtomicWord; |
| 577 BrowserThreadGlobals& globals = g_globals.Get(); | 650 BrowserThreadGlobals& globals = g_globals.Get(); |
| 578 AtomicWord* storage = reinterpret_cast<AtomicWord*>( | 651 AtomicWord* storage = reinterpret_cast<AtomicWord*>( |
| 579 &globals.thread_delegates[identifier]); | 652 &globals.thread_delegates[identifier]); |
| 580 AtomicWord old_pointer = base::subtle::NoBarrier_AtomicExchange( | 653 AtomicWord old_pointer = base::subtle::NoBarrier_AtomicExchange( |
| 581 storage, reinterpret_cast<AtomicWord>(delegate)); | 654 storage, reinterpret_cast<AtomicWord>(delegate)); |
| 582 | 655 |
| 583 // This catches registration when previously registered. | 656 // This catches registration when previously registered. |
| 584 DCHECK(!delegate || !old_pointer); | 657 DCHECK(!delegate || !old_pointer); |
| 585 } | 658 } |
| 586 | 659 |
| 587 } // namespace content | 660 } // namespace content |
| OLD | NEW |