Infobar material design refresh: bg color
[chromium-blink-merge.git] / ios / web / web_thread_impl.cc
bloba4cacddcea848af75502f0ecf28883889a606a3a
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ios/web/web_thread_impl.h"
7 #include <string>
9 #include "base/atomicops.h"
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/lazy_instance.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "ios/web/public/web_thread_delegate.h"
18 #include "net/disk_cache/simple/simple_backend_impl.h"
19 #include "net/url_request/url_fetcher.h"
21 namespace web {
23 namespace {
25 // Friendly names for the well-known threads.
26 const char* g_web_thread_names[WebThread::ID_COUNT] = {
27 "Web_UIThread", // UI
28 "Web_DBThread", // DB
29 "Web_FileThread", // FILE
30 "Web_FileUserBlockingThread", // FILE_USER_BLOCKING
31 "Web_CacheThread", // CACHE
32 "Web_IOThread", // IO
35 // An implementation of SingleThreadTaskRunner to be used in conjunction
36 // with WebThread.
37 class WebThreadTaskRunner : public base::SingleThreadTaskRunner {
38 public:
39 explicit WebThreadTaskRunner(WebThread::ID identifier) : id_(identifier) {}
41 // SingleThreadTaskRunner implementation.
42 bool PostDelayedTask(const tracked_objects::Location& from_here,
43 const base::Closure& task,
44 base::TimeDelta delay) override {
45 return WebThread::PostDelayedTask(id_, from_here, task, delay);
48 bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
49 const base::Closure& task,
50 base::TimeDelta delay) override {
51 return WebThread::PostNonNestableDelayedTask(id_, from_here, task, delay);
54 bool RunsTasksOnCurrentThread() const override {
55 return WebThread::CurrentlyOn(id_);
58 protected:
59 ~WebThreadTaskRunner() override {}
61 private:
62 WebThread::ID id_;
63 DISALLOW_COPY_AND_ASSIGN(WebThreadTaskRunner);
66 // A separate helper is used just for the task runners, in order to avoid
67 // needing to initialize the globals to create a task runner.
68 struct WebThreadTaskRunners {
69 WebThreadTaskRunners() {
70 for (int i = 0; i < WebThread::ID_COUNT; ++i) {
71 task_runners[i] = new WebThreadTaskRunner(static_cast<WebThread::ID>(i));
75 scoped_refptr<base::SingleThreadTaskRunner> task_runners[WebThread::ID_COUNT];
78 base::LazyInstance<WebThreadTaskRunners>::Leaky g_task_runners =
79 LAZY_INSTANCE_INITIALIZER;
81 struct WebThreadGlobals {
82 WebThreadGlobals()
83 : blocking_pool(new base::SequencedWorkerPool(3, "WebBlocking")) {
84 memset(threads, 0, WebThread::ID_COUNT * sizeof(threads[0]));
85 memset(thread_delegates, 0,
86 WebThread::ID_COUNT * sizeof(thread_delegates[0]));
89 // This lock protects |threads|. Do not read or modify that array
90 // without holding this lock. Do not block while holding this lock.
91 base::Lock lock;
93 // This array is protected by |lock|. The threads are not owned by this
94 // array. Typically, the threads are owned on the UI thread by
95 // WebMainLoop. WebThreadImpl objects remove themselves from this
96 // array upon destruction.
97 WebThreadImpl* threads[WebThread::ID_COUNT];
99 // Only atomic operations are used on this array. The delegates are not owned
100 // by this array, rather by whoever calls WebThread::SetDelegate.
101 WebThreadDelegate* thread_delegates[WebThread::ID_COUNT];
103 const scoped_refptr<base::SequencedWorkerPool> blocking_pool;
106 base::LazyInstance<WebThreadGlobals>::Leaky g_globals =
107 LAZY_INSTANCE_INITIALIZER;
109 } // namespace
111 WebThreadImpl::WebThreadImpl(ID identifier)
112 : Thread(g_web_thread_names[identifier]), identifier_(identifier) {
113 Initialize();
116 WebThreadImpl::WebThreadImpl(ID identifier, base::MessageLoop* message_loop)
117 : Thread(message_loop->thread_name()), identifier_(identifier) {
118 set_message_loop(message_loop);
119 Initialize();
122 // static
123 void WebThreadImpl::ShutdownThreadPool() {
124 // The goal is to make it impossible to 'infinite loop' during shutdown,
125 // but to reasonably expect that all BLOCKING_SHUTDOWN tasks queued during
126 // shutdown get run. There's nothing particularly scientific about the
127 // number chosen.
128 const int kMaxNewShutdownBlockingTasks = 1000;
129 WebThreadGlobals& globals = g_globals.Get();
130 globals.blocking_pool->Shutdown(kMaxNewShutdownBlockingTasks);
133 // static
134 void WebThreadImpl::FlushThreadPoolHelperForTesting() {
135 // We don't want to create a pool if none exists.
136 if (g_globals == nullptr)
137 return;
138 g_globals.Get().blocking_pool->FlushForTesting();
139 disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
142 void WebThreadImpl::Init() {
143 WebThreadGlobals& globals = g_globals.Get();
145 using base::subtle::AtomicWord;
146 AtomicWord* storage =
147 reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]);
148 AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage);
149 WebThreadDelegate* delegate =
150 reinterpret_cast<WebThreadDelegate*>(stored_pointer);
151 if (delegate) {
152 delegate->Init();
153 message_loop()->PostTask(FROM_HERE,
154 base::Bind(&WebThreadDelegate::InitAsync,
155 // Delegate is expected to exist for the
156 // duration of the thread's lifetime
157 base::Unretained(delegate)));
160 if (WebThread::CurrentlyOn(WebThread::IO)) {
161 // Though this thread is called the "IO" thread, it actually just routes
162 // messages around; it shouldn't be allowed to perform any blocking disk
163 // I/O.
164 base::ThreadRestrictions::SetIOAllowed(false);
165 base::ThreadRestrictions::DisallowWaiting();
169 NOINLINE void WebThreadImpl::UIThreadRun(base::MessageLoop* message_loop) {
170 volatile int line_number = __LINE__;
171 Thread::Run(message_loop);
172 CHECK_GT(line_number, 0);
175 NOINLINE void WebThreadImpl::DBThreadRun(base::MessageLoop* message_loop) {
176 volatile int line_number = __LINE__;
177 Thread::Run(message_loop);
178 CHECK_GT(line_number, 0);
181 NOINLINE void WebThreadImpl::FileThreadRun(base::MessageLoop* message_loop) {
182 volatile int line_number = __LINE__;
183 Thread::Run(message_loop);
184 CHECK_GT(line_number, 0);
187 NOINLINE void WebThreadImpl::FileUserBlockingThreadRun(
188 base::MessageLoop* message_loop) {
189 volatile int line_number = __LINE__;
190 Thread::Run(message_loop);
191 CHECK_GT(line_number, 0);
194 NOINLINE void WebThreadImpl::CacheThreadRun(base::MessageLoop* message_loop) {
195 volatile int line_number = __LINE__;
196 Thread::Run(message_loop);
197 CHECK_GT(line_number, 0);
200 NOINLINE void WebThreadImpl::IOThreadRun(base::MessageLoop* message_loop) {
201 volatile int line_number = __LINE__;
202 Thread::Run(message_loop);
203 CHECK_GT(line_number, 0);
206 void WebThreadImpl::Run(base::MessageLoop* message_loop) {
207 WebThread::ID thread_id = ID_COUNT;
208 if (!GetCurrentThreadIdentifier(&thread_id))
209 return Thread::Run(message_loop);
211 switch (thread_id) {
212 case WebThread::UI:
213 return UIThreadRun(message_loop);
214 case WebThread::DB:
215 return DBThreadRun(message_loop);
216 case WebThread::FILE:
217 return FileThreadRun(message_loop);
218 case WebThread::FILE_USER_BLOCKING:
219 return FileUserBlockingThreadRun(message_loop);
220 case WebThread::CACHE:
221 return CacheThreadRun(message_loop);
222 case WebThread::IO:
223 return IOThreadRun(message_loop);
224 case WebThread::ID_COUNT:
225 CHECK(false); // This shouldn't actually be reached!
226 break;
228 Thread::Run(message_loop);
231 void WebThreadImpl::CleanUp() {
232 if (WebThread::CurrentlyOn(WebThread::IO))
233 IOThreadPreCleanUp();
235 WebThreadGlobals& globals = g_globals.Get();
237 using base::subtle::AtomicWord;
238 AtomicWord* storage =
239 reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier_]);
240 AtomicWord stored_pointer = base::subtle::NoBarrier_Load(storage);
241 WebThreadDelegate* delegate =
242 reinterpret_cast<WebThreadDelegate*>(stored_pointer);
244 if (delegate)
245 delegate->CleanUp();
248 void WebThreadImpl::Initialize() {
249 WebThreadGlobals& globals = g_globals.Get();
251 base::AutoLock lock(globals.lock);
252 DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT);
253 DCHECK(globals.threads[identifier_] == nullptr);
254 globals.threads[identifier_] = this;
257 void WebThreadImpl::IOThreadPreCleanUp() {
258 // Kill all things that might be holding onto
259 // net::URLRequest/net::URLRequestContexts.
261 // Destroy all URLRequests started by URLFetchers.
262 net::URLFetcher::CancelAll();
265 WebThreadImpl::~WebThreadImpl() {
266 // All Thread subclasses must call Stop() in the destructor. This is
267 // doubly important here as various bits of code check they are on
268 // the right WebThread.
269 Stop();
271 WebThreadGlobals& globals = g_globals.Get();
272 base::AutoLock lock(globals.lock);
273 globals.threads[identifier_] = nullptr;
274 #ifndef NDEBUG
275 // Double check that the threads are ordered correctly in the enumeration.
276 for (int i = identifier_ + 1; i < ID_COUNT; ++i) {
277 DCHECK(!globals.threads[i])
278 << "Threads must be listed in the reverse order that they die";
280 #endif
283 // static
284 bool WebThreadImpl::PostTaskHelper(WebThread::ID identifier,
285 const tracked_objects::Location& from_here,
286 const base::Closure& task,
287 base::TimeDelta delay,
288 bool nestable) {
289 DCHECK(identifier >= 0 && identifier < ID_COUNT);
290 // Optimization: to avoid unnecessary locks, we listed the ID enumeration in
291 // order of lifetime. So no need to lock if we know that the target thread
292 // outlives current thread.
293 // Note: since the array is so small, ok to loop instead of creating a map,
294 // which would require a lock because std::map isn't thread safe, defeating
295 // the whole purpose of this optimization.
296 WebThread::ID current_thread = ID_COUNT;
297 bool target_thread_outlives_current =
298 GetCurrentThreadIdentifier(&current_thread) &&
299 current_thread >= identifier;
301 WebThreadGlobals& globals = g_globals.Get();
302 if (!target_thread_outlives_current)
303 globals.lock.Acquire();
305 base::MessageLoop* message_loop =
306 globals.threads[identifier] ? globals.threads[identifier]->message_loop()
307 : nullptr;
308 if (message_loop) {
309 if (nestable) {
310 message_loop->PostDelayedTask(from_here, task, delay);
311 } else {
312 message_loop->PostNonNestableDelayedTask(from_here, task, delay);
316 if (!target_thread_outlives_current)
317 globals.lock.Release();
319 return !!message_loop;
322 // static
323 bool WebThread::PostBlockingPoolTask(const tracked_objects::Location& from_here,
324 const base::Closure& task) {
325 return g_globals.Get().blocking_pool->PostWorkerTask(from_here, task);
328 // static
329 bool WebThread::PostBlockingPoolTaskAndReply(
330 const tracked_objects::Location& from_here,
331 const base::Closure& task,
332 const base::Closure& reply) {
333 return g_globals.Get().blocking_pool->PostTaskAndReply(from_here, task,
334 reply);
337 // static
338 bool WebThread::PostBlockingPoolSequencedTask(
339 const std::string& sequence_token_name,
340 const tracked_objects::Location& from_here,
341 const base::Closure& task) {
342 return g_globals.Get().blocking_pool->PostNamedSequencedWorkerTask(
343 sequence_token_name, from_here, task);
346 // static
347 base::SequencedWorkerPool* WebThread::GetBlockingPool() {
348 return g_globals.Get().blocking_pool.get();
351 // static
352 bool WebThread::IsThreadInitialized(ID identifier) {
353 if (g_globals == nullptr)
354 return false;
356 WebThreadGlobals& globals = g_globals.Get();
357 base::AutoLock lock(globals.lock);
358 DCHECK(identifier >= 0 && identifier < ID_COUNT);
359 return globals.threads[identifier] != nullptr;
362 // static
363 bool WebThread::CurrentlyOn(ID identifier) {
364 // This shouldn't use MessageLoop::current() since it uses LazyInstance which
365 // may be deleted by ~AtExitManager when a WorkerPool thread calls this
366 // function.
367 // http://crbug.com/63678
368 base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
369 WebThreadGlobals& globals = g_globals.Get();
370 base::AutoLock lock(globals.lock);
371 DCHECK(identifier >= 0 && identifier < ID_COUNT);
372 return globals.threads[identifier] &&
373 globals.threads[identifier]->message_loop() ==
374 base::MessageLoop::current();
377 static const char* GetThreadName(WebThread::ID thread) {
378 if (WebThread::UI <= thread && thread < WebThread::ID_COUNT)
379 return g_web_thread_names[thread];
380 return "Unknown Thread";
383 // static
384 std::string WebThread::GetDCheckCurrentlyOnErrorMessage(ID expected) {
385 const base::MessageLoop* message_loop = base::MessageLoop::current();
386 ID actual_web_thread;
387 const char* actual_name = "Unknown Thread";
388 if (message_loop && !message_loop->thread_name().empty()) {
389 actual_name = message_loop->thread_name().c_str();
390 } else if (GetCurrentThreadIdentifier(&actual_web_thread)) {
391 actual_name = GetThreadName(actual_web_thread);
393 std::string result = "Must be called on ";
394 result += GetThreadName(expected);
395 result += "; actually called on ";
396 result += actual_name;
397 result += ".";
398 return result;
401 // static
402 bool WebThread::IsMessageLoopValid(ID identifier) {
403 if (g_globals == nullptr)
404 return false;
406 WebThreadGlobals& globals = g_globals.Get();
407 base::AutoLock lock(globals.lock);
408 DCHECK(identifier >= 0 && identifier < ID_COUNT);
409 return globals.threads[identifier] &&
410 globals.threads[identifier]->message_loop();
413 // static
414 bool WebThread::PostTask(ID identifier,
415 const tracked_objects::Location& from_here,
416 const base::Closure& task) {
417 return WebThreadImpl::PostTaskHelper(identifier, from_here, task,
418 base::TimeDelta(), true);
421 // static
422 bool WebThread::PostDelayedTask(ID identifier,
423 const tracked_objects::Location& from_here,
424 const base::Closure& task,
425 base::TimeDelta delay) {
426 return WebThreadImpl::PostTaskHelper(identifier, from_here, task, delay,
427 true);
430 // static
431 bool WebThread::PostNonNestableTask(ID identifier,
432 const tracked_objects::Location& from_here,
433 const base::Closure& task) {
434 return WebThreadImpl::PostTaskHelper(identifier, from_here, task,
435 base::TimeDelta(), false);
438 // static
439 bool WebThread::PostNonNestableDelayedTask(
440 ID identifier,
441 const tracked_objects::Location& from_here,
442 const base::Closure& task,
443 base::TimeDelta delay) {
444 return WebThreadImpl::PostTaskHelper(identifier, from_here, task, delay,
445 false);
448 // static
449 bool WebThread::PostTaskAndReply(ID identifier,
450 const tracked_objects::Location& from_here,
451 const base::Closure& task,
452 const base::Closure& reply) {
453 return GetTaskRunnerForThread(identifier)
454 ->PostTaskAndReply(from_here, task, reply);
457 // static
458 bool WebThread::GetCurrentThreadIdentifier(ID* identifier) {
459 if (g_globals == nullptr)
460 return false;
462 // This shouldn't use MessageLoop::current() since it uses LazyInstance which
463 // may be deleted by ~AtExitManager when a WorkerPool thread calls this
464 // function.
465 // http://crbug.com/63678
466 base::ThreadRestrictions::ScopedAllowSingleton allow_singleton;
467 base::MessageLoop* cur_message_loop = base::MessageLoop::current();
468 WebThreadGlobals& globals = g_globals.Get();
469 for (int i = 0; i < ID_COUNT; ++i) {
470 if (globals.threads[i] &&
471 globals.threads[i]->message_loop() == cur_message_loop) {
472 *identifier = globals.threads[i]->identifier_;
473 return true;
477 return false;
480 // static
481 scoped_refptr<base::SingleThreadTaskRunner> WebThread::GetTaskRunnerForThread(
482 ID identifier) {
483 return g_task_runners.Get().task_runners[identifier];
486 // static
487 base::MessageLoop* WebThread::UnsafeGetMessageLoopForThread(ID identifier) {
488 if (g_globals == nullptr)
489 return nullptr;
491 WebThreadGlobals& globals = g_globals.Get();
492 base::AutoLock lock(globals.lock);
493 base::Thread* thread = globals.threads[identifier];
494 DCHECK(thread);
495 base::MessageLoop* loop = thread->message_loop();
496 return loop;
499 // static
500 void WebThreadImpl::SetDelegate(ID identifier, WebThreadDelegate* delegate) {
501 using base::subtle::AtomicWord;
502 WebThreadGlobals& globals = g_globals.Get();
503 AtomicWord* storage =
504 reinterpret_cast<AtomicWord*>(&globals.thread_delegates[identifier]);
505 AtomicWord old_pointer = base::subtle::NoBarrier_AtomicExchange(
506 storage, reinterpret_cast<AtomicWord>(delegate));
508 // This catches registration when previously registered.
509 DCHECK(!delegate || !old_pointer);
512 } // namespace web