xref: /aosp_15_r20/external/cronet/base/threading/thread_restrictions.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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