• 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/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