• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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 #if DCHECK_IS_ON()
8 
9 #include "base/lazy_instance.h"
10 #include "base/logging.h"
11 #include "base/threading/thread_local.h"
12 
13 namespace base {
14 
15 namespace {
16 
17 LazyInstance<ThreadLocalBoolean>::Leaky g_blocking_disallowed =
18     LAZY_INSTANCE_INITIALIZER;
19 
20 LazyInstance<ThreadLocalBoolean>::Leaky
21     g_singleton_disallowed = LAZY_INSTANCE_INITIALIZER;
22 
23 LazyInstance<ThreadLocalBoolean>::Leaky g_base_sync_primitives_disallowed =
24     LAZY_INSTANCE_INITIALIZER;
25 
26 }  // namespace
27 
AssertBlockingAllowed()28 void AssertBlockingAllowed() {
29   DCHECK(!g_blocking_disallowed.Get().Get())
30       << "Function marked as blocking was called from a scope that disallows "
31          "blocking! If this task is running inside the TaskScheduler, it needs "
32          "to have MayBlock() in its TaskTraits. Otherwise, consider making "
33          "this blocking work asynchronous or, as a last resort, you may use "
34          "ScopedAllowBlocking (see its documentation for best practices).";
35 }
36 
DisallowBlocking()37 void DisallowBlocking() {
38   g_blocking_disallowed.Get().Set(true);
39 }
40 
ScopedDisallowBlocking()41 ScopedDisallowBlocking::ScopedDisallowBlocking()
42     : was_disallowed_(g_blocking_disallowed.Get().Get()) {
43   g_blocking_disallowed.Get().Set(true);
44 }
45 
~ScopedDisallowBlocking()46 ScopedDisallowBlocking::~ScopedDisallowBlocking() {
47   DCHECK(g_blocking_disallowed.Get().Get());
48   g_blocking_disallowed.Get().Set(was_disallowed_);
49 }
50 
ScopedAllowBlocking()51 ScopedAllowBlocking::ScopedAllowBlocking()
52     : was_disallowed_(g_blocking_disallowed.Get().Get()) {
53   g_blocking_disallowed.Get().Set(false);
54 }
55 
~ScopedAllowBlocking()56 ScopedAllowBlocking::~ScopedAllowBlocking() {
57   DCHECK(!g_blocking_disallowed.Get().Get());
58   g_blocking_disallowed.Get().Set(was_disallowed_);
59 }
60 
DisallowBaseSyncPrimitives()61 void DisallowBaseSyncPrimitives() {
62   g_base_sync_primitives_disallowed.Get().Set(true);
63 }
64 
ScopedAllowBaseSyncPrimitives()65 ScopedAllowBaseSyncPrimitives::ScopedAllowBaseSyncPrimitives()
66     : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) {
67   DCHECK(!g_blocking_disallowed.Get().Get())
68       << "To allow //base sync primitives in a scope where blocking is "
69          "disallowed use ScopedAllowBaseSyncPrimitivesOutsideBlockingScope.";
70   g_base_sync_primitives_disallowed.Get().Set(false);
71 }
72 
~ScopedAllowBaseSyncPrimitives()73 ScopedAllowBaseSyncPrimitives::~ScopedAllowBaseSyncPrimitives() {
74   DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
75   g_base_sync_primitives_disallowed.Get().Set(was_disallowed_);
76 }
77 
78 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope()79     ScopedAllowBaseSyncPrimitivesOutsideBlockingScope()
80     : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) {
81   g_base_sync_primitives_disallowed.Get().Set(false);
82 }
83 
84 ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope()85     ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() {
86   DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
87   g_base_sync_primitives_disallowed.Get().Set(was_disallowed_);
88 }
89 
90 ScopedAllowBaseSyncPrimitivesForTesting::
ScopedAllowBaseSyncPrimitivesForTesting()91     ScopedAllowBaseSyncPrimitivesForTesting()
92     : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) {
93   g_base_sync_primitives_disallowed.Get().Set(false);
94 }
95 
96 ScopedAllowBaseSyncPrimitivesForTesting::
~ScopedAllowBaseSyncPrimitivesForTesting()97     ~ScopedAllowBaseSyncPrimitivesForTesting() {
98   DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
99   g_base_sync_primitives_disallowed.Get().Set(was_disallowed_);
100 }
101 
102 namespace internal {
103 
AssertBaseSyncPrimitivesAllowed()104 void AssertBaseSyncPrimitivesAllowed() {
105   DCHECK(!g_base_sync_primitives_disallowed.Get().Get())
106       << "Waiting on a //base sync primitive is not allowed on this thread to "
107          "prevent jank and deadlock. If waiting on a //base sync primitive is "
108          "unavoidable, do it within the scope of a "
109          "ScopedAllowBaseSyncPrimitives. If in a test, "
110          "use ScopedAllowBaseSyncPrimitivesForTesting.";
111 }
112 
ResetThreadRestrictionsForTesting()113 void ResetThreadRestrictionsForTesting() {
114   g_blocking_disallowed.Get().Set(false);
115   g_singleton_disallowed.Get().Set(false);
116   g_base_sync_primitives_disallowed.Get().Set(false);
117 }
118 
119 }  // namespace internal
120 
ScopedAllowIO()121 ThreadRestrictions::ScopedAllowIO::ScopedAllowIO()
122     : was_allowed_(SetIOAllowed(true)) {}
123 
~ScopedAllowIO()124 ThreadRestrictions::ScopedAllowIO::~ScopedAllowIO() {
125   SetIOAllowed(was_allowed_);
126 }
127 
128 // static
SetIOAllowed(bool allowed)129 bool ThreadRestrictions::SetIOAllowed(bool allowed) {
130   bool previous_disallowed = g_blocking_disallowed.Get().Get();
131   g_blocking_disallowed.Get().Set(!allowed);
132   return !previous_disallowed;
133 }
134 
135 // static
SetSingletonAllowed(bool allowed)136 bool ThreadRestrictions::SetSingletonAllowed(bool allowed) {
137   bool previous_disallowed = g_singleton_disallowed.Get().Get();
138   g_singleton_disallowed.Get().Set(!allowed);
139   return !previous_disallowed;
140 }
141 
142 // static
AssertSingletonAllowed()143 void ThreadRestrictions::AssertSingletonAllowed() {
144   if (g_singleton_disallowed.Get().Get()) {
145     NOTREACHED() << "LazyInstance/Singleton is not allowed to be used on this "
146                  << "thread.  Most likely it's because this thread is not "
147                  << "joinable (or the current task is running with "
148                  << "TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN semantics), so "
149                  << "AtExitManager may have deleted the object on shutdown, "
150                  << "leading to a potential shutdown crash. If you need to use "
151                  << "the object from this context, it'll have to be updated to "
152                  << "use Leaky traits.";
153   }
154 }
155 
156 // static
DisallowWaiting()157 void ThreadRestrictions::DisallowWaiting() {
158   DisallowBaseSyncPrimitives();
159 }
160 
SetWaitAllowed(bool allowed)161 bool ThreadRestrictions::SetWaitAllowed(bool allowed) {
162   bool previous_disallowed = g_base_sync_primitives_disallowed.Get().Get();
163   g_base_sync_primitives_disallowed.Get().Set(!allowed);
164   return !previous_disallowed;
165 }
166 
ScopedAllowWait()167 ThreadRestrictions::ScopedAllowWait::ScopedAllowWait()
168     : was_allowed_(SetWaitAllowed(true)) {}
169 
~ScopedAllowWait()170 ThreadRestrictions::ScopedAllowWait::~ScopedAllowWait() {
171   SetWaitAllowed(was_allowed_);
172 }
173 
174 }  // namespace base
175 
176 #endif  // DCHECK_IS_ON()
177