/Users/deen/code/yugabyte-db/src/yb/gutil/threading/thread_collision_warner.h
Line | Count | Source (jump to first uncovered line) |
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 | | // The following only applies to changes made to this file as part of YugaByte development. |
6 | | // |
7 | | // Portions Copyright (c) YugaByte, Inc. |
8 | | // |
9 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
10 | | // in compliance with the License. You may obtain a copy of the License at |
11 | | // |
12 | | // http://www.apache.org/licenses/LICENSE-2.0 |
13 | | // |
14 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
15 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
16 | | // or implied. See the License for the specific language governing permissions and limitations |
17 | | // under the License. |
18 | | // |
19 | | |
20 | | #ifndef YB_GUTIL_THREADING_THREAD_COLLISION_WARNER_H |
21 | | #define YB_GUTIL_THREADING_THREAD_COLLISION_WARNER_H |
22 | | |
23 | | #include "yb/gutil/atomicops.h" |
24 | | #include "yb/gutil/macros.h" |
25 | | #include "yb/gutil/port.h" |
26 | | |
27 | | #ifndef BASE_EXPORT |
28 | | #define BASE_EXPORT |
29 | | #endif |
30 | | |
31 | | // A helper class alongside macros to be used to verify assumptions about thread |
32 | | // safety of a class. |
33 | | // |
34 | | // Example: Queue implementation non thread-safe but still usable if clients |
35 | | // are synchronized somehow. |
36 | | // |
37 | | // In this case the macro DFAKE_SCOPED_LOCK has to be |
38 | | // used, it checks that if a thread is inside the push/pop then |
39 | | // noone else is still inside the pop/push |
40 | | // |
41 | | // class NonThreadSafeQueue { |
42 | | // public: |
43 | | // ... |
44 | | // void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... } |
45 | | // int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... } |
46 | | // ... |
47 | | // private: |
48 | | // DFAKE_MUTEX(push_pop_); |
49 | | // }; |
50 | | // |
51 | | // |
52 | | // Example: Queue implementation non thread-safe but still usable if clients |
53 | | // are synchronized somehow, it calls a method to "protect" from |
54 | | // a "protected" method |
55 | | // |
56 | | // In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK |
57 | | // has to be used, it checks that if a thread is inside the push/pop |
58 | | // then noone else is still inside the pop/push |
59 | | // |
60 | | // class NonThreadSafeQueue { |
61 | | // public: |
62 | | // void push(int) { |
63 | | // DFAKE_SCOPED_LOCK(push_pop_); |
64 | | // ... |
65 | | // } |
66 | | // int pop() { |
67 | | // DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); |
68 | | // bar(); |
69 | | // ... |
70 | | // } |
71 | | // void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... } |
72 | | // ... |
73 | | // private: |
74 | | // DFAKE_MUTEX(push_pop_); |
75 | | // }; |
76 | | // |
77 | | // |
78 | | // Example: Queue implementation not usable even if clients are synchronized, |
79 | | // so only one thread in the class life cycle can use the two members |
80 | | // push/pop. |
81 | | // |
82 | | // In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the |
83 | | // specified |
84 | | // critical section the first time a thread enters push or pop, from |
85 | | // that time on only that thread is allowed to execute push or pop. |
86 | | // |
87 | | // class NonThreadSafeQueue { |
88 | | // public: |
89 | | // ... |
90 | | // void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } |
91 | | // int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } |
92 | | // ... |
93 | | // private: |
94 | | // DFAKE_MUTEX(push_pop_); |
95 | | // }; |
96 | | // |
97 | | // |
98 | | // Example: Class that has to be contructed/destroyed on same thread, it has |
99 | | // a "shareable" method (with external synchronization) and a not |
100 | | // shareable method (even with external synchronization). |
101 | | // |
102 | | // In this case 3 Critical sections have to be defined |
103 | | // |
104 | | // class ExoticClass { |
105 | | // public: |
106 | | // ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } |
107 | | // ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } |
108 | | // |
109 | | // void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... } |
110 | | // void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } |
111 | | // ... |
112 | | // private: |
113 | | // DFAKE_MUTEX(ctor_dtor_); |
114 | | // DFAKE_MUTEX(shareable_section_); |
115 | | // }; |
116 | | |
117 | | |
118 | | #if !defined(NDEBUG) |
119 | | |
120 | | // Defines a class member that acts like a mutex. It is used only as a |
121 | | // verification tool. |
122 | | #define DFAKE_MUTEX(obj) \ |
123 | | mutable base::ThreadCollisionWarner obj |
124 | | // Asserts the call is never called simultaneously in two threads. Used at |
125 | | // member function scope. |
126 | | #define DFAKE_SCOPED_LOCK(obj) \ |
127 | 13.1M | base::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj) |
128 | | // Asserts the call is never called simultaneously in two threads. Used at |
129 | | // member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks. |
130 | | #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \ |
131 | | base::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj) |
132 | | // Asserts the code is always executed in the same thread. |
133 | | #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \ |
134 | 27.0M | base::ThreadCollisionWarner::Check check_##obj(&obj) |
135 | | |
136 | | #else |
137 | | |
138 | | #define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##obj |
139 | | #define DFAKE_SCOPED_LOCK(obj) ((void)0) |
140 | | #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0) |
141 | | #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0) |
142 | | |
143 | | #endif |
144 | | |
145 | | namespace base { |
146 | | |
147 | | // The class ThreadCollisionWarner uses an Asserter to notify the collision |
148 | | // AsserterBase is the interfaces and DCheckAsserter is the default asserter |
149 | | // used. During the unit tests is used another class that doesn't "DCHECK" |
150 | | // in case of collision (check thread_collision_warner_unittests.cc) |
151 | | struct BASE_EXPORT AsserterBase { |
152 | 121M | virtual ~AsserterBase() {} |
153 | | virtual void warn() = 0; |
154 | | }; |
155 | | |
156 | | struct BASE_EXPORT DCheckAsserter : public AsserterBase { |
157 | 121M | virtual ~DCheckAsserter() {} |
158 | | virtual void warn() override; |
159 | | }; |
160 | | |
161 | | class BASE_EXPORT ThreadCollisionWarner { |
162 | | public: |
163 | | // The parameter asserter is there only for test purpose |
164 | | explicit ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter()) |
165 | | : valid_thread_id_(0), |
166 | | counter_(0), |
167 | 121M | asserter_(asserter) {} |
168 | | |
169 | 121M | ~ThreadCollisionWarner() { |
170 | 121M | delete asserter_; |
171 | 121M | } |
172 | | |
173 | | // This class is meant to be used through the macro |
174 | | // DFAKE_SCOPED_LOCK_THREAD_LOCKED |
175 | | // it doesn't leave the critical section, as opposed to ScopedCheck, |
176 | | // because the critical section being pinned is allowed to be used only |
177 | | // from one thread |
178 | | class BASE_EXPORT Check { |
179 | | public: |
180 | | explicit Check(ThreadCollisionWarner* warner) |
181 | 27.0M | : warner_(warner) { |
182 | 27.0M | warner_->EnterSelf(); |
183 | 27.0M | } |
184 | | |
185 | 27.0M | ~Check() {} |
186 | | |
187 | | private: |
188 | | ThreadCollisionWarner* warner_; |
189 | | |
190 | | DISALLOW_COPY_AND_ASSIGN(Check); |
191 | | }; |
192 | | |
193 | | // This class is meant to be used through the macro |
194 | | // DFAKE_SCOPED_LOCK |
195 | | class BASE_EXPORT ScopedCheck { |
196 | | public: |
197 | | explicit ScopedCheck(ThreadCollisionWarner* warner) |
198 | 13.1M | : warner_(warner) { |
199 | 13.1M | warner_->Enter(); |
200 | 13.1M | } |
201 | | |
202 | 13.1M | ~ScopedCheck() { |
203 | 13.1M | warner_->Leave(); |
204 | 13.1M | } |
205 | | |
206 | | private: |
207 | | ThreadCollisionWarner* warner_; |
208 | | |
209 | | DISALLOW_COPY_AND_ASSIGN(ScopedCheck); |
210 | | }; |
211 | | |
212 | | // This class is meant to be used through the macro |
213 | | // DFAKE_SCOPED_RECURSIVE_LOCK |
214 | | class BASE_EXPORT ScopedRecursiveCheck { |
215 | | public: |
216 | | explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner) |
217 | 0 | : warner_(warner) { |
218 | 0 | warner_->EnterSelf(); |
219 | 0 | } |
220 | | |
221 | 0 | ~ScopedRecursiveCheck() { |
222 | 0 | warner_->Leave(); |
223 | 0 | } |
224 | | |
225 | | private: |
226 | | ThreadCollisionWarner* warner_; |
227 | | |
228 | | DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck); |
229 | | }; |
230 | | |
231 | | private: |
232 | | // This method stores the current thread identifier and does a DCHECK |
233 | | // if a another thread has already done it, it is safe if same thread |
234 | | // calls this multiple time (recursion allowed). |
235 | | void EnterSelf(); |
236 | | |
237 | | // Same as EnterSelf but recursion is not allowed. |
238 | | void Enter(); |
239 | | |
240 | | // Removes the thread_id stored in order to allow other threads to |
241 | | // call EnterSelf or Enter. |
242 | | void Leave(); |
243 | | |
244 | | // This stores the thread id that is inside the critical section, if the |
245 | | // value is 0 then no thread is inside. |
246 | | volatile subtle::Atomic64 valid_thread_id_; |
247 | | |
248 | | // Counter to trace how many time a critical section was "pinned" |
249 | | // (when allowed) in order to unpin it when counter_ reaches 0. |
250 | | volatile subtle::Atomic64 counter_; |
251 | | |
252 | | // Here only for class unit tests purpose, during the test I need to not |
253 | | // DCHECK but notify the collision with something else. |
254 | | AsserterBase* asserter_; |
255 | | |
256 | | DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner); |
257 | | }; |
258 | | |
259 | | } // namespace base |
260 | | |
261 | | #endif // YB_GUTIL_THREADING_THREAD_COLLISION_WARNER_H |