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