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