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"
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"
25 // Friendly names for the well-known threads.
26 const char* g_web_thread_names
[WebThread::ID_COUNT
] = {
29 "Web_FileThread", // FILE
30 "Web_FileUserBlockingThread", // FILE_USER_BLOCKING
31 "Web_CacheThread", // CACHE
35 // An implementation of SingleThreadTaskRunner to be used in conjunction
37 class WebThreadTaskRunner
: public base::SingleThreadTaskRunner
{
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_
);
59 ~WebThreadTaskRunner() override
{}
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
{
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.
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
;
111 WebThreadImpl::WebThreadImpl(ID identifier
)
112 : Thread(g_web_thread_names
[identifier
]), identifier_(identifier
) {
116 WebThreadImpl::WebThreadImpl(ID identifier
, base::MessageLoop
* message_loop
)
117 : Thread(message_loop
->thread_name()), identifier_(identifier
) {
118 set_message_loop(message_loop
);
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
128 const int kMaxNewShutdownBlockingTasks
= 1000;
129 WebThreadGlobals
& globals
= g_globals
.Get();
130 globals
.blocking_pool
->Shutdown(kMaxNewShutdownBlockingTasks
);
134 void WebThreadImpl::FlushThreadPoolHelperForTesting() {
135 // We don't want to create a pool if none exists.
136 if (g_globals
== nullptr)
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
);
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
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
);
213 return UIThreadRun(message_loop
);
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
);
223 return IOThreadRun(message_loop
);
224 case WebThread::ID_COUNT
:
225 CHECK(false); // This shouldn't actually be reached!
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
);
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.
271 WebThreadGlobals
& globals
= g_globals
.Get();
272 base::AutoLock
lock(globals
.lock
);
273 globals
.threads
[identifier_
] = nullptr;
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";
284 bool WebThreadImpl::PostTaskHelper(WebThread::ID identifier
,
285 const tracked_objects::Location
& from_here
,
286 const base::Closure
& task
,
287 base::TimeDelta delay
,
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(¤t_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()
310 message_loop
->PostDelayedTask(from_here
, task
, delay
);
312 message_loop
->PostNonNestableDelayedTask(from_here
, task
, delay
);
316 if (!target_thread_outlives_current
)
317 globals
.lock
.Release();
319 return !!message_loop
;
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
);
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
,
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
);
347 base::SequencedWorkerPool
* WebThread::GetBlockingPool() {
348 return g_globals
.Get().blocking_pool
.get();
352 bool WebThread::IsThreadInitialized(ID identifier
) {
353 if (g_globals
== nullptr)
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;
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
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";
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
;
402 bool WebThread::IsMessageLoopValid(ID identifier
) {
403 if (g_globals
== nullptr)
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();
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);
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
,
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);
439 bool WebThread::PostNonNestableDelayedTask(
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
,
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
);
458 bool WebThread::GetCurrentThreadIdentifier(ID
* identifier
) {
459 if (g_globals
== nullptr)
462 // This shouldn't use MessageLoop::current() since it uses LazyInstance which
463 // may be deleted by ~AtExitManager when a WorkerPool thread calls this
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_
;
481 scoped_refptr
<base::SingleThreadTaskRunner
> WebThread::GetTaskRunnerForThread(
483 return g_task_runners
.Get().task_runners
[identifier
];
487 base::MessageLoop
* WebThread::UnsafeGetMessageLoopForThread(ID identifier
) {
488 if (g_globals
== nullptr)
491 WebThreadGlobals
& globals
= g_globals
.Get();
492 base::AutoLock
lock(globals
.lock
);
493 base::Thread
* thread
= globals
.threads
[identifier
];
495 base::MessageLoop
* loop
= thread
->message_loop();
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
);