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
3 // found in the LICENSE file.
4
5 #include "content/browser/browser_thread_impl.h"
6
7 #include <string>
8 #include <utility>
9
10 #include "base/atomicops.h"
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/check_op.h"
14 #include "base/compiler_specific.h"
15 #include "base/macros.h"
16 #include "base/no_destructor.h"
17 #include "base/sequence_checker.h"
18 #include "base/task/current_thread.h"
19 #include "base/task/post_task.h"
20 #include "base/task/task_executor.h"
21 #include "base/threading/platform_thread.h"
22 #include "base/time/time.h"
23 #include "build/build_config.h"
24 #include "content/browser/scheduler/browser_task_executor.h"
25 #include "content/public/browser/browser_task_traits.h"
26 #include "content/public/browser/content_browser_client.h"
27
28 namespace content {
29
30 namespace {
31
32 // State of a given BrowserThread::ID in chronological order throughout the
33 // browser process' lifetime.
34 enum BrowserThreadState {
35 // BrowserThread::ID isn't associated with anything yet.
36 UNINITIALIZED = 0,
37 // BrowserThread::ID is associated to a TaskRunner and is accepting tasks.
38 RUNNING,
39 // BrowserThread::ID no longer accepts tasks (it's still associated to a
40 // TaskRunner but that TaskRunner doesn't have to accept tasks).
41 SHUTDOWN
42 };
43
44 struct BrowserThreadGlobals {
BrowserThreadGlobalscontent::__anon393d8ac20111::BrowserThreadGlobals45 BrowserThreadGlobals() {
46 // A few unit tests which do not use a BrowserTaskEnvironment still invoke
47 // code that reaches into CurrentlyOn()/IsThreadInitialized(). This can
48 // result in instantiating BrowserThreadGlobals off the main thread.
49 // |main_thread_checker_| being bound incorrectly would then result in a
50 // flake in the next test that instantiates a BrowserTaskEnvironment in the
51 // same process. Detaching here postpones binding |main_thread_checker_| to
52 // the first invocation of BrowserThreadImpl::BrowserThreadImpl() and works
53 // around this issue.
54 DETACH_FROM_THREAD(main_thread_checker_);
55 }
56
57 // BrowserThreadGlobals must be initialized on main thread before it's used by
58 // any other threads.
59 THREAD_CHECKER(main_thread_checker_);
60
61 // |task_runners[id]| is safe to access on |main_thread_checker_| as
62 // well as on any thread once it's read-only after initialization
63 // (i.e. while |states[id] >= RUNNING|).
64 scoped_refptr<base::SingleThreadTaskRunner>
65 task_runners[BrowserThread::ID_COUNT];
66
67 // Tracks the runtime state of BrowserThreadImpls. Atomic because a few
68 // methods below read this value outside |main_thread_checker_| to
69 // confirm it's >= RUNNING and doing so requires an atomic read as it could be
70 // in the middle of transitioning to SHUTDOWN (which the check is fine with
71 // but reading a non-atomic value as it's written to by another thread can
72 // result in undefined behaviour on some platforms).
73 // Only NoBarrier atomic operations should be used on |states| as it shouldn't
74 // be used to establish happens-after relationships but rather checking the
75 // runtime state of various threads (once again: it's only atomic to support
76 // reading while transitioning from RUNNING=>SHUTDOWN).
77 base::subtle::Atomic32 states[BrowserThread::ID_COUNT] = {};
78 };
79
GetBrowserThreadGlobals()80 BrowserThreadGlobals& GetBrowserThreadGlobals() {
81 static base::NoDestructor<BrowserThreadGlobals> globals;
82 return *globals;
83 }
84
85 } // namespace
86
GetUIThreadTaskRunner(const BrowserTaskTraits & traits)87 scoped_refptr<base::SingleThreadTaskRunner> GetUIThreadTaskRunner(
88 const BrowserTaskTraits& traits) {
89 return BrowserTaskExecutor::GetUIThreadTaskRunner(traits);
90 }
91
GetIOThreadTaskRunner(const BrowserTaskTraits & traits)92 scoped_refptr<base::SingleThreadTaskRunner> GetIOThreadTaskRunner(
93 const BrowserTaskTraits& traits) {
94 return BrowserTaskExecutor::GetIOThreadTaskRunner(traits);
95 }
96
BrowserThreadImpl(ID identifier,scoped_refptr<base::SingleThreadTaskRunner> task_runner)97 BrowserThreadImpl::BrowserThreadImpl(
98 ID identifier,
99 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
100 : identifier_(identifier) {
101 DCHECK_GE(identifier_, 0);
102 DCHECK_LT(identifier_, ID_COUNT);
103 DCHECK(task_runner);
104
105 BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
106
107 DCHECK_CALLED_ON_VALID_THREAD(globals.main_thread_checker_);
108
109 DCHECK_EQ(base::subtle::NoBarrier_Load(&globals.states[identifier_]),
110 BrowserThreadState::UNINITIALIZED);
111 base::subtle::NoBarrier_Store(&globals.states[identifier_],
112 BrowserThreadState::RUNNING);
113
114 DCHECK(!globals.task_runners[identifier_]);
115 globals.task_runners[identifier_] = std::move(task_runner);
116
117 if (identifier_ == BrowserThread::ID::UI) {
118 #if defined(OS_POSIX)
119 // Allow usage of the FileDescriptorWatcher API on the UI thread, using the
120 // IO thread to watch the file descriptors.
121 //
122 // In unit tests, usage of the FileDescriptorWatcher API is already allowed
123 // if the UI thread is running a MessageLoopForIO.
124 if (!base::CurrentIOThread::IsSet()) {
125 file_descriptor_watcher_.emplace(GetIOThreadTaskRunner({}));
126 }
127 base::FileDescriptorWatcher::AssertAllowed();
128 #endif
129 }
130 }
131
~BrowserThreadImpl()132 BrowserThreadImpl::~BrowserThreadImpl() {
133 BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
134 DCHECK_CALLED_ON_VALID_THREAD(globals.main_thread_checker_);
135
136 DCHECK_EQ(base::subtle::NoBarrier_Load(&globals.states[identifier_]),
137 BrowserThreadState::RUNNING);
138 base::subtle::NoBarrier_Store(&globals.states[identifier_],
139 BrowserThreadState::SHUTDOWN);
140
141 // The mapping is kept alive after shutdown to avoid requiring a lock only for
142 // shutdown (the SingleThreadTaskRunner itself may stop accepting tasks at any
143 // point -- usually soon before/after destroying the BrowserThreadImpl).
144 DCHECK(globals.task_runners[identifier_]);
145 }
146
147 // static
ResetGlobalsForTesting(BrowserThread::ID identifier)148 void BrowserThreadImpl::ResetGlobalsForTesting(BrowserThread::ID identifier) {
149 BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
150 DCHECK_CALLED_ON_VALID_THREAD(globals.main_thread_checker_);
151
152 DCHECK_EQ(base::subtle::NoBarrier_Load(&globals.states[identifier]),
153 BrowserThreadState::SHUTDOWN);
154 base::subtle::NoBarrier_Store(&globals.states[identifier],
155 BrowserThreadState::UNINITIALIZED);
156
157 globals.task_runners[identifier] = nullptr;
158 }
159
160 // static
GetThreadName(BrowserThread::ID thread)161 const char* BrowserThreadImpl::GetThreadName(BrowserThread::ID thread) {
162 static const char* const kBrowserThreadNames[BrowserThread::ID_COUNT] = {
163 "", // UI (name assembled in browser_main_loop.cc).
164 "Chrome_IOThread", // IO
165 };
166
167 if (BrowserThread::UI < thread && thread < BrowserThread::ID_COUNT)
168 return kBrowserThreadNames[thread];
169 if (thread == BrowserThread::UI)
170 return "Chrome_UIThread";
171 return "Unknown Thread";
172 }
173
174 // static
IsThreadInitialized(ID identifier)175 bool BrowserThread::IsThreadInitialized(ID identifier) {
176 DCHECK_GE(identifier, 0);
177 DCHECK_LT(identifier, ID_COUNT);
178
179 BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
180 return base::subtle::NoBarrier_Load(&globals.states[identifier]) ==
181 BrowserThreadState::RUNNING;
182 }
183
184 // static
CurrentlyOn(ID identifier)185 bool BrowserThread::CurrentlyOn(ID identifier) {
186 DCHECK_GE(identifier, 0);
187 DCHECK_LT(identifier, ID_COUNT);
188
189 BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
190
191 // Thread-safe since |globals.task_runners| is read-only after being
192 // initialized from main thread (which happens before //content and embedders
193 // are kicked off and enabled to call the BrowserThread API from other
194 // threads).
195 return globals.task_runners[identifier] &&
196 globals.task_runners[identifier]->RunsTasksInCurrentSequence();
197 }
198
199 // static
GetDCheckCurrentlyOnErrorMessage(ID expected)200 std::string BrowserThread::GetDCheckCurrentlyOnErrorMessage(ID expected) {
201 std::string actual_name = base::PlatformThread::GetName();
202 if (actual_name.empty())
203 actual_name = "Unknown Thread";
204
205 std::string result = "Must be called on ";
206 result += BrowserThreadImpl::GetThreadName(expected);
207 result += "; actually called on ";
208 result += actual_name;
209 result += ".";
210 return result;
211 }
212
213 // static
GetCurrentThreadIdentifier(ID * identifier)214 bool BrowserThread::GetCurrentThreadIdentifier(ID* identifier) {
215 BrowserThreadGlobals& globals = GetBrowserThreadGlobals();
216
217 // Thread-safe since |globals.task_runners| is read-only after being
218 // initialized from main thread (which happens before //content and embedders
219 // are kicked off and enabled to call the BrowserThread API from other
220 // threads).
221 for (int i = 0; i < ID_COUNT; ++i) {
222 if (globals.task_runners[i] &&
223 globals.task_runners[i]->RunsTasksInCurrentSequence()) {
224 *identifier = static_cast<ID>(i);
225 return true;
226 }
227 }
228
229 return false;
230 }
231
232 // static
233 scoped_refptr<base::SingleThreadTaskRunner>
GetTaskRunnerForThread(ID identifier)234 BrowserThread::GetTaskRunnerForThread(ID identifier) {
235 DCHECK_GE(identifier, 0);
236 DCHECK_LT(identifier, ID_COUNT);
237 return base::CreateSingleThreadTaskRunner({identifier});
238 }
239
240 // static
RunAllPendingTasksOnThreadForTesting(ID identifier)241 void BrowserThread::RunAllPendingTasksOnThreadForTesting(ID identifier) {
242 BrowserTaskExecutor::RunAllPendingTasksOnThreadForTesting(identifier);
243 }
244
245 // static
PostBestEffortTask(const base::Location & from_here,scoped_refptr<base::TaskRunner> task_runner,base::OnceClosure task)246 void BrowserThread::PostBestEffortTask(
247 const base::Location& from_here,
248 scoped_refptr<base::TaskRunner> task_runner,
249 base::OnceClosure task) {
250 content::GetIOThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
251 ->PostTask(
252 FROM_HERE,
253 base::BindOnce(base::IgnoreResult(&base::TaskRunner::PostTask),
254 std::move(task_runner), from_here, std::move(task)));
255 }
256
257 } // namespace content
258