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