thread_restrictions.cc (11638B)
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 28 BooleanWithStack::BooleanWithStack(bool value) : value_(value) { 29 if (kCaptureStackTraces) { 30 stack_.emplace(); 31 } 32 } 33 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. 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 } 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 } 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 } 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 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 97 void AssertBlockingDisallowedForTesting() { 98 DCHECK(GetBlockingDisallowedTls()) 99 << "blocking_disallowed " << GetBlockingDisallowedTls(); 100 } 101 102 } // namespace internal 103 104 void DisallowBlocking() { 105 GetBlockingDisallowedTls() = BooleanWithStack(true); 106 } 107 108 ScopedDisallowBlocking::ScopedDisallowBlocking() 109 : resetter_(&GetBlockingDisallowedTls(), BooleanWithStack(true)) {} 110 111 ScopedDisallowBlocking::~ScopedDisallowBlocking() { 112 DCHECK(GetBlockingDisallowedTls()) 113 << "~ScopedDisallowBlocking() running while surprisingly already no " 114 "longer disallowed.\n" 115 << "blocking_disallowed " << GetBlockingDisallowedTls(); 116 } 117 118 void DisallowBaseSyncPrimitives() { 119 GetBaseSyncPrimitivesDisallowedTls() = BooleanWithStack(true); 120 } 121 122 ScopedDisallowBaseSyncPrimitives::ScopedDisallowBaseSyncPrimitives() 123 : resetter_(&GetBaseSyncPrimitivesDisallowedTls(), BooleanWithStack(true)) { 124 } 125 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 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 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:: 152 ScopedAllowBaseSyncPrimitivesForTesting() 153 : resetter_(&GetBaseSyncPrimitivesDisallowedTls(), 154 BooleanWithStack(false)) {} 155 156 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 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:: 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 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 204 void ResetThreadRestrictionsForTesting() { 205 GetBlockingDisallowedTls() = BooleanWithStack(false); 206 GetSingletonDisallowedTls() = BooleanWithStack(false); 207 GetBaseSyncPrimitivesDisallowedTls() = BooleanWithStack(false); 208 GetCPUIntensiveWorkDisallowedTls() = BooleanWithStack(false); 209 } 210 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 225 void DisallowSingleton() { 226 GetSingletonDisallowedTls() = BooleanWithStack(true); 227 } 228 229 ScopedDisallowSingleton::ScopedDisallowSingleton() 230 : resetter_(&GetSingletonDisallowedTls(), BooleanWithStack(true)) {} 231 232 ScopedDisallowSingleton::~ScopedDisallowSingleton() { 233 DCHECK(GetSingletonDisallowedTls()) 234 << "~ScopedDisallowSingleton() running while surprisingly already no " 235 "longer disallowed.\n" 236 << "singleton_disallowed " << GetSingletonDisallowedTls(); 237 } 238 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 247 void DisallowUnresponsiveTasks() { 248 DisallowBlocking(); 249 DisallowBaseSyncPrimitives(); 250 GetCPUIntensiveWorkDisallowedTls() = BooleanWithStack(true); 251 } 252 253 // static 254 void PermanentThreadAllowance::AllowBlocking() { 255 GetBlockingDisallowedTls() = BooleanWithStack(false); 256 } 257 258 // static 259 void PermanentThreadAllowance::AllowBaseSyncPrimitives() { 260 GetBaseSyncPrimitivesDisallowedTls() = BooleanWithStack(false); 261 } 262 263 } // namespace base 264 265 #endif // DCHECK_IS_ON() 266 267 namespace base { 268 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 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 #if !defined(MOZ_SANDBOX) 293 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope:: 294 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(const Location& from_here) 295 #if DCHECK_IS_ON() 296 : resetter_(&GetBaseSyncPrimitivesDisallowedTls(), BooleanWithStack(false)) 297 #endif 298 { 299 TRACE_EVENT_BEGIN( 300 "base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope", 301 [&](perfetto::EventContext ctx) { 302 ctx.event()->set_source_location_iid( 303 base::trace_event::InternedSourceLocation::Get(&ctx, from_here)); 304 }); 305 306 // Since this object is used to indicate that sync primitives will be used to 307 // wait for an event ignore the current operation for hang watching purposes 308 // since the wait time duration is unknown. 309 base::HangWatcher::InvalidateActiveExpectations(); 310 } 311 #endif 312 313 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope:: 314 ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() { 315 TRACE_EVENT_END0("base", "ScopedAllowBaseSyncPrimitivesOutsideBlockingScope"); 316 317 #if DCHECK_IS_ON() 318 DCHECK(!GetBaseSyncPrimitivesDisallowedTls()) 319 << "~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() running while " 320 "surprisingly already no longer allowed.\n" 321 << "base_sync_primitives_disallowed " 322 << GetBaseSyncPrimitivesDisallowedTls(); 323 #endif 324 } 325 326 } // namespace base