1 // Copyright 2012 The Chromium Authors
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 "base/threading/thread_restrictions.h"
6
7 #include "base/threading/hang_watcher.h"
8 #include "base/trace_event/base_tracing.h"
9 #include "build/build_config.h"
10
11 #if DCHECK_IS_ON()
12 #include "base/check_op.h"
13 #include "third_party/abseil-cpp/absl/base/attributes.h"
14
15 // NaCL doesn't support stack sampling and Android is slow at stack sampling and
16 // this causes timeouts (crbug.com/959139).
17 #if BUILDFLAG(IS_NACL) || BUILDFLAG(IS_ANDROID)
18 constexpr bool kCaptureStackTraces = false;
19 #else
20 // Always disabled when !EXPENSIVE_DCHECKS_ARE_ON() because user-facing builds
21 // typically drop log strings anyways.
22 constexpr bool kCaptureStackTraces = EXPENSIVE_DCHECKS_ARE_ON();
23 #endif
24
25 namespace base {
26
BooleanWithStack(bool value)27 BooleanWithStack::BooleanWithStack(bool value) : value_(value) {
28 if (kCaptureStackTraces) {
29 stack_.emplace();
30 }
31 }
32
operator <<(std::ostream & out,const BooleanWithStack & bws)33 std::ostream& operator<<(std::ostream& out, const BooleanWithStack& bws) {
34 out << bws.value_;
35 if (kCaptureStackTraces) {
36 if (bws.stack_.has_value()) {
37 out << " set by\n" << bws.stack_.value();
38 } else {
39 out << " (value by default)";
40 }
41 }
42 return out;
43 }
44
45 namespace {
46
47 ABSL_CONST_INIT thread_local BooleanWithStack tls_blocking_disallowed;
48 ABSL_CONST_INIT thread_local BooleanWithStack tls_singleton_disallowed;
49 ABSL_CONST_INIT thread_local BooleanWithStack
50 tls_base_sync_primitives_disallowed;
51 ABSL_CONST_INIT thread_local BooleanWithStack tls_cpu_intensive_work_disallowed;
52
53 } // namespace
54
55 namespace internal {
56
AssertBlockingAllowed()57 void AssertBlockingAllowed() {
58 DCHECK(!tls_blocking_disallowed)
59 << "Function marked as blocking was called from a scope that disallows "
60 "blocking! If this task is running inside the ThreadPool, it needs "
61 "to have MayBlock() in its TaskTraits. Otherwise, consider making "
62 "this blocking work asynchronous or, as a last resort, you may use "
63 "ScopedAllowBlocking (see its documentation for best practices).\n"
64 << "tls_blocking_disallowed " << tls_blocking_disallowed;
65 }
66
AssertBlockingDisallowedForTesting()67 void AssertBlockingDisallowedForTesting() {
68 DCHECK(tls_blocking_disallowed)
69 << "tls_blocking_disallowed " << tls_blocking_disallowed;
70 }
71
72 } // namespace internal
73
DisallowBlocking()74 void DisallowBlocking() {
75 tls_blocking_disallowed = BooleanWithStack(true);
76 }
77
ScopedDisallowBlocking()78 ScopedDisallowBlocking::ScopedDisallowBlocking()
79 : resetter_(&tls_blocking_disallowed, BooleanWithStack(true)) {}
80
~ScopedDisallowBlocking()81 ScopedDisallowBlocking::~ScopedDisallowBlocking() {
82 DCHECK(tls_blocking_disallowed)
83 << "~ScopedDisallowBlocking() running while surprisingly already no "
84 "longer disallowed.\n"
85 << "tls_blocking_disallowed " << tls_blocking_disallowed;
86 }
87
DisallowBaseSyncPrimitives()88 void DisallowBaseSyncPrimitives() {
89 tls_base_sync_primitives_disallowed = BooleanWithStack(true);
90 }
91
ScopedDisallowBaseSyncPrimitives()92 ScopedDisallowBaseSyncPrimitives::ScopedDisallowBaseSyncPrimitives()
93 : resetter_(&tls_base_sync_primitives_disallowed, BooleanWithStack(true)) {}
94
~ScopedDisallowBaseSyncPrimitives()95 ScopedDisallowBaseSyncPrimitives::~ScopedDisallowBaseSyncPrimitives() {
96 DCHECK(tls_base_sync_primitives_disallowed)
97 << "~ScopedDisallowBaseSyncPrimitives() running while surprisingly "
98 "already no longer disallowed.\n"
99 << "tls_base_sync_primitives_disallowed "
100 << tls_base_sync_primitives_disallowed;
101 }
102
ScopedAllowBaseSyncPrimitives()103 ScopedAllowBaseSyncPrimitives::ScopedAllowBaseSyncPrimitives()
104 : resetter_(&tls_base_sync_primitives_disallowed, BooleanWithStack(false)) {
105 DCHECK(!tls_blocking_disallowed)
106 << "To allow //base sync primitives in a scope where blocking is "
107 "disallowed use ScopedAllowBaseSyncPrimitivesOutsideBlockingScope.\n"
108 << "tls_blocking_disallowed " << tls_blocking_disallowed;
109 }
110
~ScopedAllowBaseSyncPrimitives()111 ScopedAllowBaseSyncPrimitives::~ScopedAllowBaseSyncPrimitives() {
112 DCHECK(!tls_base_sync_primitives_disallowed)
113 << "~ScopedAllowBaseSyncPrimitives() running while surprisingly already "
114 "no longer allowed.\n"
115 << "tls_base_sync_primitives_disallowed "
116 << tls_base_sync_primitives_disallowed;
117 }
118
119 ScopedAllowBaseSyncPrimitivesForTesting::
ScopedAllowBaseSyncPrimitivesForTesting()120 ScopedAllowBaseSyncPrimitivesForTesting()
121 : resetter_(&tls_base_sync_primitives_disallowed, BooleanWithStack(false)) {
122 }
123
124 ScopedAllowBaseSyncPrimitivesForTesting::
~ScopedAllowBaseSyncPrimitivesForTesting()125 ~ScopedAllowBaseSyncPrimitivesForTesting() {
126 DCHECK(!tls_base_sync_primitives_disallowed)
127 << "~ScopedAllowBaseSyncPrimitivesForTesting() running while " // IN-TEST
128 "surprisingly already no longer allowed.\n"
129 << "tls_base_sync_primitives_disallowed "
130 << tls_base_sync_primitives_disallowed;
131 }
132
ScopedAllowUnresponsiveTasksForTesting()133 ScopedAllowUnresponsiveTasksForTesting::ScopedAllowUnresponsiveTasksForTesting()
134 : base_sync_resetter_(&tls_base_sync_primitives_disallowed,
135 BooleanWithStack(false)),
136 blocking_resetter_(&tls_blocking_disallowed, BooleanWithStack(false)),
137 cpu_resetter_(&tls_cpu_intensive_work_disallowed,
138 BooleanWithStack(false)) {}
139
140 ScopedAllowUnresponsiveTasksForTesting::
~ScopedAllowUnresponsiveTasksForTesting()141 ~ScopedAllowUnresponsiveTasksForTesting() {
142 DCHECK(!tls_base_sync_primitives_disallowed)
143 << "~ScopedAllowUnresponsiveTasksForTesting() running while " // IN-TEST
144 "surprisingly already no longer allowed.\n"
145 << "tls_base_sync_primitives_disallowed "
146 << tls_base_sync_primitives_disallowed;
147 DCHECK(!tls_blocking_disallowed)
148 << "~ScopedAllowUnresponsiveTasksForTesting() running while " // IN-TEST
149 "surprisingly already no longer allowed.\n"
150 << "tls_blocking_disallowed " << tls_blocking_disallowed;
151 DCHECK(!tls_cpu_intensive_work_disallowed)
152 << "~ScopedAllowUnresponsiveTasksForTesting() running while " // IN-TEST
153 "surprisingly already no longer allowed.\n"
154 << "tls_cpu_intensive_work_disallowed "
155 << tls_cpu_intensive_work_disallowed;
156 }
157
158 namespace internal {
159
AssertBaseSyncPrimitivesAllowed()160 void AssertBaseSyncPrimitivesAllowed() {
161 DCHECK(!tls_base_sync_primitives_disallowed)
162 << "Waiting on a //base sync primitive is not allowed on this thread to "
163 "prevent jank and deadlock. If waiting on a //base sync primitive is "
164 "unavoidable, do it within the scope of a "
165 "ScopedAllowBaseSyncPrimitives. If in a test, use "
166 "ScopedAllowBaseSyncPrimitivesForTesting.\n"
167 << "tls_base_sync_primitives_disallowed "
168 << tls_base_sync_primitives_disallowed
169 << "It can be useful to know that tls_blocking_disallowed is "
170 << tls_blocking_disallowed;
171 }
172
ResetThreadRestrictionsForTesting()173 void ResetThreadRestrictionsForTesting() {
174 tls_blocking_disallowed = BooleanWithStack(false);
175 tls_singleton_disallowed = BooleanWithStack(false);
176 tls_base_sync_primitives_disallowed = BooleanWithStack(false);
177 tls_cpu_intensive_work_disallowed = BooleanWithStack(false);
178 }
179
AssertSingletonAllowed()180 void AssertSingletonAllowed() {
181 DCHECK(!tls_singleton_disallowed)
182 << "LazyInstance/Singleton is not allowed to be used on this thread. "
183 "Most likely it's because this thread is not joinable (or the current "
184 "task is running with TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN "
185 "semantics), so AtExitManager may have deleted the object on "
186 "shutdown, leading to a potential shutdown crash. If you need to use "
187 "the object from this context, it'll have to be updated to use Leaky "
188 "traits.\n"
189 << "tls_singleton_disallowed " << tls_singleton_disallowed;
190 }
191
192 } // namespace internal
193
DisallowSingleton()194 void DisallowSingleton() {
195 tls_singleton_disallowed = BooleanWithStack(true);
196 }
197
ScopedDisallowSingleton()198 ScopedDisallowSingleton::ScopedDisallowSingleton()
199 : resetter_(&tls_singleton_disallowed, BooleanWithStack(true)) {}
200
~ScopedDisallowSingleton()201 ScopedDisallowSingleton::~ScopedDisallowSingleton() {
202 DCHECK(tls_singleton_disallowed)
203 << "~ScopedDisallowSingleton() running while surprisingly already no "
204 "longer disallowed.\n"
205 << "tls_singleton_disallowed " << tls_singleton_disallowed;
206 }
207
AssertLongCPUWorkAllowed()208 void AssertLongCPUWorkAllowed() {
209 DCHECK(!tls_cpu_intensive_work_disallowed)
210 << "Function marked as CPU intensive was called from a scope that "
211 "disallows this kind of work! Consider making this work "
212 "asynchronous.\n"
213 << "tls_cpu_intensive_work_disallowed "
214 << tls_cpu_intensive_work_disallowed;
215 }
216
DisallowUnresponsiveTasks()217 void DisallowUnresponsiveTasks() {
218 DisallowBlocking();
219 DisallowBaseSyncPrimitives();
220 tls_cpu_intensive_work_disallowed = BooleanWithStack(true);
221 }
222
223 // static
AllowBlocking()224 void PermanentThreadAllowance::AllowBlocking() {
225 tls_blocking_disallowed = BooleanWithStack(false);
226 }
227
228 // static
AllowBaseSyncPrimitives()229 void PermanentThreadAllowance::AllowBaseSyncPrimitives() {
230 tls_base_sync_primitives_disallowed = BooleanWithStack(false);
231 }
232
233 } // namespace base
234
235 #endif // DCHECK_IS_ON()
236
237 namespace base {
238
ScopedAllowBlocking(const Location & from_here)239 ScopedAllowBlocking::ScopedAllowBlocking(const Location& from_here)
240 #if DCHECK_IS_ON()
241 : resetter_(&tls_blocking_disallowed, BooleanWithStack(false))
242 #endif
243 {
244 TRACE_EVENT_BEGIN(
245 "base", "ScopedAllowBlocking", [&](perfetto::EventContext ctx) {
246 ctx.event()->set_source_location_iid(
247 base::trace_event::InternedSourceLocation::Get(&ctx, from_here));
248 });
249 }
250
~ScopedAllowBlocking()251 ScopedAllowBlocking::~ScopedAllowBlocking() {
252 TRACE_EVENT_END0("base", "ScopedAllowBlocking");
253
254 #if DCHECK_IS_ON()
255 DCHECK(!tls_blocking_disallowed)
256 << "~ScopedAllowBlocking() running while surprisingly already no longer "
257 "allowed.\n"
258 << "tls_blocking_disallowed " << tls_blocking_disallowed;
259 #endif
260 }
261
262 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(const Location & from_here)263 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(const Location& from_here)
264 #if DCHECK_IS_ON()
265 : resetter_(&tls_base_sync_primitives_disallowed, BooleanWithStack(false))
266 #endif
267 {
268 TRACE_EVENT_BEGIN(
269 "base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope",
270 [&](perfetto::EventContext ctx) {
271 ctx.event()->set_source_location_iid(
272 base::trace_event::InternedSourceLocation::Get(&ctx, from_here));
273 });
274
275 // Since this object is used to indicate that sync primitives will be used to
276 // wait for an event ignore the current operation for hang watching purposes
277 // since the wait time duration is unknown.
278 base::HangWatcher::InvalidateActiveExpectations();
279 }
280
281 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope()282 ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() {
283 TRACE_EVENT_END0("base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope");
284
285 #if DCHECK_IS_ON()
286 DCHECK(!tls_base_sync_primitives_disallowed)
287 << "~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() running while "
288 "surprisingly already no longer allowed.\n"
289 << "tls_base_sync_primitives_disallowed "
290 << tls_base_sync_primitives_disallowed;
291 #endif
292 }
293
294 } // namespace base
295