1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
content / public / browser / browser_thread.h [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_PUBLIC_BROWSER_BROWSER_THREAD_H_
#define CONTENT_PUBLIC_BROWSER_BROWSER_THREAD_H_
#include <memory>
#include <string>
#include <utility>
#include "base/check.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/macros/uniquify.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/single_thread_task_runner.h"
#include "base/thread_annotations.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_task_traits.h"
#if defined(UNIT_TEST)
#include "base/logging.h"
#endif
namespace content {
// Use DCHECK_CURRENTLY_ON(BrowserThread::ID) to DCHECK that a function can only
// be called on the named BrowserThread.
#define DCHECK_CURRENTLY_ON(thread_identifier) \
::content::internal::ScopedValidateBrowserThreadDebugChecker BASE_UNIQUIFY( \
scoped_validate_browser_thread_dchecker_)(thread_identifier)
// Use CHECK_CURRENTLY_ON(BrowserThread::ID) to CHECK that a function can only
// be called on the named BrowserThread.
#define CHECK_CURRENTLY_ON(thread_identifier, ...) \
::content::internal::ScopedValidateBrowserThreadChecker BASE_UNIQUIFY( \
scoped_validate_browser_thread_checker_)( \
thread_identifier __VA_OPT__(, ) __VA_ARGS__)
// GUARDED_BY_BROWSER_THREAD() enforces that a member variable is only accessed
// from a scope that invokes DCHECK_CURRENTLY_ON() or CHECK_CURRENTLY_ON() or
// from a function annotated with VALID_BROWSER_THREAD_REQUIRED(). The code will
// not compile if the member variable is accessed and these conditions are not
// met.
#define GUARDED_BY_BROWSER_THREAD(thread_identifier) \
GUARDED_BY(::content::internal::GetBrowserThreadChecker(thread_identifier))
// VALID_CONTEXT_REQUIRED() enforces that a member function is only accessed
// from a scope that invokes DCHECK_CURRENTLY_ON() or CHECK_CURRENTLY_ON() or
// from another function annotated with VALID_BROWSER_THREAD_REQUIRED(). The
// code will not compile if the member function is accessed and these conditions
// are not met.
#define VALID_BROWSER_THREAD_REQUIRED(thread_identifier) \
EXCLUSIVE_LOCKS_REQUIRED( \
::content::internal::GetBrowserThreadChecker(thread_identifier))
// The main entry point to post tasks to the UI thread. Tasks posted with the
// same |traits| will run in posting order (i.e. according to the
// SequencedTaskRunner contract). Tasks posted with different |traits| can be
// re-ordered. You may keep a reference to this task runner, it's always
// thread-safe to post to it though it may start returning false at some point
// during shutdown when it definitely is no longer accepting tasks.
//
// In unit tests, there must be a content::BrowserTaskEnvironment in scope for
// this API to be available.
CONTENT_EXPORT scoped_refptr<base::SingleThreadTaskRunner>
GetUIThreadTaskRunner(const BrowserTaskTraits& traits = {});
// The BrowserThread::IO counterpart to GetUIThreadTaskRunner().
CONTENT_EXPORT scoped_refptr<base::SingleThreadTaskRunner>
GetIOThreadTaskRunner(const BrowserTaskTraits& traits = {});
///////////////////////////////////////////////////////////////////////////////
// BrowserThread
//
// Utility functions for threads that are known by a browser-wide name.
class CONTENT_EXPORT BrowserThread {
public:
// An enumeration of the well-known threads.
enum ID {
// The main thread in the browser. It stops running tasks during shutdown
// and is never joined.
UI,
// This is the thread that processes non-blocking I/O, i.e. IPC and network.
// Blocking I/O should happen in base::ThreadPool. It is joined on shutdown
// (and thus any task posted to it may block shutdown).
//
// The name is admittedly confusing, as the IO thread is not for blocking
// I/O like calling base::File::Read. "The highly responsive, non-blocking
// I/O thread for IPC" is more accurate but too long for an enum name. See
// docs/transcripts/wuwt-e08-processes.md at 44:20 for more history.
IO,
// NOTE: do not add new threads here. Instead you should just use
// base::ThreadPool::Create*TaskRunner to run tasks on the base::ThreadPool.
// This identifier does not represent a thread. Instead it counts the
// number of well-known threads. Insert new well-known threads before this
// identifier.
ID_COUNT
};
BrowserThread(const BrowserThread&) = delete;
BrowserThread& operator=(const BrowserThread&) = delete;
// Delete/ReleaseSoon() helpers allow future deletion of an owned object on
// its associated thread. If you already have a task runner bound to a
// BrowserThread you should use its SequencedTaskRunner::DeleteSoon() member
// method.
// TODO(crbug.com/40108370): Get rid of the last few callers to these in favor
// of an explicit call to GetUIThreadTaskRunner({})->DeleteSoon(...).
template <class T>
static bool DeleteSoon(ID identifier,
const base::Location& from_here,
const T* object) {
return GetTaskRunnerForThread(identifier)->DeleteSoon(from_here, object);
}
template <class T>
static bool DeleteSoon(ID identifier,
const base::Location& from_here,
std::unique_ptr<T> object) {
return DeleteSoon(identifier, from_here, object.release());
}
template <class T>
static void ReleaseSoon(ID identifier,
const base::Location& from_here,
scoped_refptr<T>&& object) {
GetTaskRunnerForThread(identifier)
->ReleaseSoon(from_here, std::move(object));
}
// Posts a |task| to run at BEST_EFFORT priority using an arbitrary
// |task_runner| for which we do not control the priority.
//
// This is useful when a task needs to run on |task_runner| (for thread-safety
// reasons) but should be delayed until after critical phases (e.g. startup).
// TODO(crbug.com/40553790): Add support for sequence-funneling and remove
// this method.
static void PostBestEffortTask(const base::Location& from_here,
scoped_refptr<base::TaskRunner> task_runner,
base::OnceClosure task);
// Callable on any thread. Returns whether the given well-known thread is
// initialized.
[[nodiscard]] static bool IsThreadInitialized(ID identifier);
// Callable on any thread. Returns whether you're currently on a particular
// thread. To DCHECK this, use the DCHECK_CURRENTLY_ON() macro above.
[[nodiscard]] static bool CurrentlyOn(ID identifier);
// If the current message loop is one of the known threads, returns true and
// sets identifier to its ID. Otherwise returns false.
[[nodiscard]] static bool GetCurrentThreadIdentifier(ID* identifier);
// Use these templates in conjunction with RefCountedThreadSafe or scoped_ptr
// when you want to ensure that an object is deleted on a specific thread.
// This is needed when an object can hop between threads (i.e. UI -> IO ->
// UI), and thread switching delays can mean that the final UI tasks executes
// before the IO task's stack unwinds. This would lead to the object
// destructing on the IO thread, which often is not what you want (i.e. to
// notify other objects on the creating thread etc). Note: see
// base::OnTaskRunnerDeleter and base::RefCountedDeleteOnSequence to bind to
// SequencedTaskRunner instead of specific BrowserThreads.
template <ID thread>
struct DeleteOnThread {
template <typename T>
static void Destruct(const T* x) {
if (CurrentlyOn(thread)) {
delete x;
} else {
if (!DeleteSoon(thread, FROM_HERE, x)) {
#if defined(UNIT_TEST)
// Only logged under unit testing because leaks at shutdown
// are acceptable under normal circumstances.
LOG(ERROR) << "DeleteSoon failed on thread " << thread;
#endif // UNIT_TEST
}
}
}
template <typename T>
inline void operator()(T* ptr) const {
enum { type_must_be_complete = sizeof(T) };
Destruct(ptr);
}
};
// Sample usage with RefCountedThreadSafe:
// class Foo
// : public base::RefCountedThreadSafe<
// Foo, BrowserThread::DeleteOnIOThread> {
//
// ...
// private:
// friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
// friend class base::DeleteHelper<Foo>;
//
// ~Foo();
//
// Sample usage with scoped_ptr:
// std::unique_ptr<Foo, BrowserThread::DeleteOnIOThread> ptr;
//
// Note: see base::OnTaskRunnerDeleter and base::RefCountedDeleteOnSequence to
// bind to SequencedTaskRunner instead of specific BrowserThreads.
struct DeleteOnUIThread : public DeleteOnThread<UI> {};
struct DeleteOnIOThread : public DeleteOnThread<IO> {};
// Returns an appropriate error message for when DCHECK_CURRENTLY_ON() fails.
static std::string GetCurrentlyOnErrorMessage(ID expected);
// Runs all pending tasks for the given thread. Tasks posted after this method
// is called (in particular any task posted from within any of the pending
// tasks) will be queued but not run. Conceptually this call will disable all
// queues, run any pending tasks, and re-enable all the queues.
//
// If any of the pending tasks posted a task, these could be run by calling
// this method again or running a regular RunLoop. But if that were the case
// you should probably rewrite you tests to wait for a specific event instead.
//
// NOTE: Can only be called from the UI thread.
static void RunAllPendingTasksOnThreadForTesting(ID identifier);
// Helper that returns GetUIThreadTaskRunner({}) or GetIOThreadTaskRunner({})
// based on |identifier|. Requires that the BrowserThread with the provided
// |identifier| was started.
static scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunnerForThread(
ID identifier);
private:
friend class BrowserThreadImpl;
BrowserThread() = default;
};
namespace internal {
class THREAD_ANNOTATION_ATTRIBUTE__(capability("BrowserThread checker"))
CONTENT_EXPORT BrowserThreadChecker {
public:
[[nodiscard]] bool CalledOnValidBrowserThread(
BrowserThread::ID thread_identifier) const;
};
// Returns the global BrowserThreadChecker associated with `thread_identifier`.
CONTENT_EXPORT const BrowserThreadChecker& GetBrowserThreadChecker(
BrowserThread::ID thread_identifier);
// CHECK version.
class CONTENT_EXPORT SCOPED_LOCKABLE ScopedValidateBrowserThreadChecker {
public:
explicit ScopedValidateBrowserThreadChecker(
BrowserThread::ID thread_identifier,
base::NotFatalUntil fatal_milestone =
base::NotFatalUntil::NoSpecifiedMilestoneInternal)
EXCLUSIVE_LOCK_FUNCTION(GetBrowserThreadChecker(thread_identifier));
~ScopedValidateBrowserThreadChecker() UNLOCK_FUNCTION();
};
// DCHECK version.
// Note: When DCHECKs are disabled, this class needs to be completely optimized
// out in order to not regress binary size. This is achieved by inlining the
// constructor and the destructor. When DCHECKs are enabled, the constructor
// is not unnecessarily inlined.
class CONTENT_EXPORT SCOPED_LOCKABLE ScopedValidateBrowserThreadDebugChecker {
public:
explicit ScopedValidateBrowserThreadDebugChecker(
BrowserThread::ID thread_identifier)
EXCLUSIVE_LOCK_FUNCTION(GetBrowserThreadChecker(thread_identifier))
// Only inlined when DCHECKs are turned off.
#if DCHECK_IS_ON()
;
#else
{
}
#endif
// Note: Can't use = default as it does not work well with UNLOCK_FUNCTION().
// Clang will discard the UNLOCK_FUNCTION() attribute.
// See https://github.com/llvm/llvm-project/issues/101199.
~ScopedValidateBrowserThreadDebugChecker() UNLOCK_FUNCTION() {}
};
} // namespace internal
} // namespace content
#endif // CONTENT_PUBLIC_BROWSER_BROWSER_THREAD_H_