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