annotate clang/docs/ThreadSafetyAnalysis.rst @ 168:980e56f2e095

...
author anatofuz
date Tue, 14 Apr 2020 15:43:20 +0900
parents 1d019706d866
children 2e18cbf3894f
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1
anatofuz
parents:
diff changeset
2 ======================
anatofuz
parents:
diff changeset
3 Thread Safety Analysis
anatofuz
parents:
diff changeset
4 ======================
anatofuz
parents:
diff changeset
5
anatofuz
parents:
diff changeset
6 Introduction
anatofuz
parents:
diff changeset
7 ============
anatofuz
parents:
diff changeset
8
anatofuz
parents:
diff changeset
9 Clang Thread Safety Analysis is a C++ language extension which warns about
anatofuz
parents:
diff changeset
10 potential race conditions in code. The analysis is completely static (i.e.
anatofuz
parents:
diff changeset
11 compile-time); there is no run-time overhead. The analysis is still
anatofuz
parents:
diff changeset
12 under active development, but it is mature enough to be deployed in an
anatofuz
parents:
diff changeset
13 industrial setting. It is being developed by Google, in collaboration with
anatofuz
parents:
diff changeset
14 CERT/SEI, and is used extensively in Google's internal code base.
anatofuz
parents:
diff changeset
15
anatofuz
parents:
diff changeset
16 Thread safety analysis works very much like a type system for multi-threaded
anatofuz
parents:
diff changeset
17 programs. In addition to declaring the *type* of data (e.g. ``int``, ``float``,
anatofuz
parents:
diff changeset
18 etc.), the programmer can (optionally) declare how access to that data is
anatofuz
parents:
diff changeset
19 controlled in a multi-threaded environment. For example, if ``foo`` is
anatofuz
parents:
diff changeset
20 *guarded by* the mutex ``mu``, then the analysis will issue a warning whenever
anatofuz
parents:
diff changeset
21 a piece of code reads or writes to ``foo`` without first locking ``mu``.
anatofuz
parents:
diff changeset
22 Similarly, if there are particular routines that should only be called by
anatofuz
parents:
diff changeset
23 the GUI thread, then the analysis will warn if other threads call those
anatofuz
parents:
diff changeset
24 routines.
anatofuz
parents:
diff changeset
25
anatofuz
parents:
diff changeset
26 Getting Started
anatofuz
parents:
diff changeset
27 ----------------
anatofuz
parents:
diff changeset
28
anatofuz
parents:
diff changeset
29 .. code-block:: c++
anatofuz
parents:
diff changeset
30
anatofuz
parents:
diff changeset
31 #include "mutex.h"
anatofuz
parents:
diff changeset
32
anatofuz
parents:
diff changeset
33 class BankAccount {
anatofuz
parents:
diff changeset
34 private:
anatofuz
parents:
diff changeset
35 Mutex mu;
anatofuz
parents:
diff changeset
36 int balance GUARDED_BY(mu);
anatofuz
parents:
diff changeset
37
anatofuz
parents:
diff changeset
38 void depositImpl(int amount) {
anatofuz
parents:
diff changeset
39 balance += amount; // WARNING! Cannot write balance without locking mu.
anatofuz
parents:
diff changeset
40 }
anatofuz
parents:
diff changeset
41
anatofuz
parents:
diff changeset
42 void withdrawImpl(int amount) REQUIRES(mu) {
anatofuz
parents:
diff changeset
43 balance -= amount; // OK. Caller must have locked mu.
anatofuz
parents:
diff changeset
44 }
anatofuz
parents:
diff changeset
45
anatofuz
parents:
diff changeset
46 public:
anatofuz
parents:
diff changeset
47 void withdraw(int amount) {
anatofuz
parents:
diff changeset
48 mu.Lock();
anatofuz
parents:
diff changeset
49 withdrawImpl(amount); // OK. We've locked mu.
anatofuz
parents:
diff changeset
50 } // WARNING! Failed to unlock mu.
anatofuz
parents:
diff changeset
51
anatofuz
parents:
diff changeset
52 void transferFrom(BankAccount& b, int amount) {
anatofuz
parents:
diff changeset
53 mu.Lock();
anatofuz
parents:
diff changeset
54 b.withdrawImpl(amount); // WARNING! Calling withdrawImpl() requires locking b.mu.
anatofuz
parents:
diff changeset
55 depositImpl(amount); // OK. depositImpl() has no requirements.
anatofuz
parents:
diff changeset
56 mu.Unlock();
anatofuz
parents:
diff changeset
57 }
anatofuz
parents:
diff changeset
58 };
anatofuz
parents:
diff changeset
59
anatofuz
parents:
diff changeset
60 This example demonstrates the basic concepts behind the analysis. The
anatofuz
parents:
diff changeset
61 ``GUARDED_BY`` attribute declares that a thread must lock ``mu`` before it can
anatofuz
parents:
diff changeset
62 read or write to ``balance``, thus ensuring that the increment and decrement
anatofuz
parents:
diff changeset
63 operations are atomic. Similarly, ``REQUIRES`` declares that
anatofuz
parents:
diff changeset
64 the calling thread must lock ``mu`` before calling ``withdrawImpl``.
anatofuz
parents:
diff changeset
65 Because the caller is assumed to have locked ``mu``, it is safe to modify
anatofuz
parents:
diff changeset
66 ``balance`` within the body of the method.
anatofuz
parents:
diff changeset
67
anatofuz
parents:
diff changeset
68 The ``depositImpl()`` method does not have ``REQUIRES``, so the
anatofuz
parents:
diff changeset
69 analysis issues a warning. Thread safety analysis is not inter-procedural, so
anatofuz
parents:
diff changeset
70 caller requirements must be explicitly declared.
anatofuz
parents:
diff changeset
71 There is also a warning in ``transferFrom()``, because although the method
anatofuz
parents:
diff changeset
72 locks ``this->mu``, it does not lock ``b.mu``. The analysis understands
anatofuz
parents:
diff changeset
73 that these are two separate mutexes, in two different objects.
anatofuz
parents:
diff changeset
74
anatofuz
parents:
diff changeset
75 Finally, there is a warning in the ``withdraw()`` method, because it fails to
anatofuz
parents:
diff changeset
76 unlock ``mu``. Every lock must have a corresponding unlock, and the analysis
anatofuz
parents:
diff changeset
77 will detect both double locks, and double unlocks. A function is allowed to
anatofuz
parents:
diff changeset
78 acquire a lock without releasing it, (or vice versa), but it must be annotated
anatofuz
parents:
diff changeset
79 as such (using ``ACQUIRE``/``RELEASE``).
anatofuz
parents:
diff changeset
80
anatofuz
parents:
diff changeset
81
anatofuz
parents:
diff changeset
82 Running The Analysis
anatofuz
parents:
diff changeset
83 --------------------
anatofuz
parents:
diff changeset
84
anatofuz
parents:
diff changeset
85 To run the analysis, simply compile with the ``-Wthread-safety`` flag, e.g.
anatofuz
parents:
diff changeset
86
anatofuz
parents:
diff changeset
87 .. code-block:: bash
anatofuz
parents:
diff changeset
88
anatofuz
parents:
diff changeset
89 clang -c -Wthread-safety example.cpp
anatofuz
parents:
diff changeset
90
anatofuz
parents:
diff changeset
91 Note that this example assumes the presence of a suitably annotated
anatofuz
parents:
diff changeset
92 :ref:`mutexheader` that declares which methods perform locking,
anatofuz
parents:
diff changeset
93 unlocking, and so on.
anatofuz
parents:
diff changeset
94
anatofuz
parents:
diff changeset
95
anatofuz
parents:
diff changeset
96 Basic Concepts: Capabilities
anatofuz
parents:
diff changeset
97 ============================
anatofuz
parents:
diff changeset
98
anatofuz
parents:
diff changeset
99 Thread safety analysis provides a way of protecting *resources* with
anatofuz
parents:
diff changeset
100 *capabilities*. A resource is either a data member, or a function/method
anatofuz
parents:
diff changeset
101 that provides access to some underlying resource. The analysis ensures that
anatofuz
parents:
diff changeset
102 the calling thread cannot access the *resource* (i.e. call the function, or
anatofuz
parents:
diff changeset
103 read/write the data) unless it has the *capability* to do so.
anatofuz
parents:
diff changeset
104
anatofuz
parents:
diff changeset
105 Capabilities are associated with named C++ objects which declare specific
anatofuz
parents:
diff changeset
106 methods to acquire and release the capability. The name of the object serves
anatofuz
parents:
diff changeset
107 to identify the capability. The most common example is a mutex. For example,
anatofuz
parents:
diff changeset
108 if ``mu`` is a mutex, then calling ``mu.Lock()`` causes the calling thread
anatofuz
parents:
diff changeset
109 to acquire the capability to access data that is protected by ``mu``. Similarly,
anatofuz
parents:
diff changeset
110 calling ``mu.Unlock()`` releases that capability.
anatofuz
parents:
diff changeset
111
anatofuz
parents:
diff changeset
112 A thread may hold a capability either *exclusively* or *shared*. An exclusive
anatofuz
parents:
diff changeset
113 capability can be held by only one thread at a time, while a shared capability
anatofuz
parents:
diff changeset
114 can be held by many threads at the same time. This mechanism enforces a
anatofuz
parents:
diff changeset
115 multiple-reader, single-writer pattern. Write operations to protected data
anatofuz
parents:
diff changeset
116 require exclusive access, while read operations require only shared access.
anatofuz
parents:
diff changeset
117
anatofuz
parents:
diff changeset
118 At any given moment during program execution, a thread holds a specific set of
anatofuz
parents:
diff changeset
119 capabilities (e.g. the set of mutexes that it has locked.) These act like keys
anatofuz
parents:
diff changeset
120 or tokens that allow the thread to access a given resource. Just like physical
anatofuz
parents:
diff changeset
121 security keys, a thread cannot make copy of a capability, nor can it destroy
anatofuz
parents:
diff changeset
122 one. A thread can only release a capability to another thread, or acquire one
anatofuz
parents:
diff changeset
123 from another thread. The annotations are deliberately agnostic about the
anatofuz
parents:
diff changeset
124 exact mechanism used to acquire and release capabilities; it assumes that the
anatofuz
parents:
diff changeset
125 underlying implementation (e.g. the Mutex implementation) does the handoff in
anatofuz
parents:
diff changeset
126 an appropriate manner.
anatofuz
parents:
diff changeset
127
anatofuz
parents:
diff changeset
128 The set of capabilities that are actually held by a given thread at a given
anatofuz
parents:
diff changeset
129 point in program execution is a run-time concept. The static analysis works
anatofuz
parents:
diff changeset
130 by calculating an approximation of that set, called the *capability
anatofuz
parents:
diff changeset
131 environment*. The capability environment is calculated for every program point,
anatofuz
parents:
diff changeset
132 and describes the set of capabilities that are statically known to be held, or
anatofuz
parents:
diff changeset
133 not held, at that particular point. This environment is a conservative
anatofuz
parents:
diff changeset
134 approximation of the full set of capabilities that will actually held by a
anatofuz
parents:
diff changeset
135 thread at run-time.
anatofuz
parents:
diff changeset
136
anatofuz
parents:
diff changeset
137
anatofuz
parents:
diff changeset
138 Reference Guide
anatofuz
parents:
diff changeset
139 ===============
anatofuz
parents:
diff changeset
140
anatofuz
parents:
diff changeset
141 The thread safety analysis uses attributes to declare threading constraints.
anatofuz
parents:
diff changeset
142 Attributes must be attached to named declarations, such as classes, methods,
anatofuz
parents:
diff changeset
143 and data members. Users are *strongly advised* to define macros for the various
anatofuz
parents:
diff changeset
144 attributes; example definitions can be found in :ref:`mutexheader`, below.
anatofuz
parents:
diff changeset
145 The following documentation assumes the use of macros.
anatofuz
parents:
diff changeset
146
anatofuz
parents:
diff changeset
147 For historical reasons, prior versions of thread safety used macro names that
anatofuz
parents:
diff changeset
148 were very lock-centric. These macros have since been renamed to fit a more
anatofuz
parents:
diff changeset
149 general capability model. The prior names are still in use, and will be
anatofuz
parents:
diff changeset
150 mentioned under the tag *previously* where appropriate.
anatofuz
parents:
diff changeset
151
anatofuz
parents:
diff changeset
152
anatofuz
parents:
diff changeset
153 GUARDED_BY(c) and PT_GUARDED_BY(c)
anatofuz
parents:
diff changeset
154 ----------------------------------
anatofuz
parents:
diff changeset
155
anatofuz
parents:
diff changeset
156 ``GUARDED_BY`` is an attribute on data members, which declares that the data
anatofuz
parents:
diff changeset
157 member is protected by the given capability. Read operations on the data
anatofuz
parents:
diff changeset
158 require shared access, while write operations require exclusive access.
anatofuz
parents:
diff changeset
159
anatofuz
parents:
diff changeset
160 ``PT_GUARDED_BY`` is similar, but is intended for use on pointers and smart
anatofuz
parents:
diff changeset
161 pointers. There is no constraint on the data member itself, but the *data that
anatofuz
parents:
diff changeset
162 it points to* is protected by the given capability.
anatofuz
parents:
diff changeset
163
anatofuz
parents:
diff changeset
164 .. code-block:: c++
anatofuz
parents:
diff changeset
165
anatofuz
parents:
diff changeset
166 Mutex mu;
anatofuz
parents:
diff changeset
167 int *p1 GUARDED_BY(mu);
anatofuz
parents:
diff changeset
168 int *p2 PT_GUARDED_BY(mu);
anatofuz
parents:
diff changeset
169 unique_ptr<int> p3 PT_GUARDED_BY(mu);
anatofuz
parents:
diff changeset
170
anatofuz
parents:
diff changeset
171 void test() {
anatofuz
parents:
diff changeset
172 p1 = 0; // Warning!
anatofuz
parents:
diff changeset
173
anatofuz
parents:
diff changeset
174 *p2 = 42; // Warning!
anatofuz
parents:
diff changeset
175 p2 = new int; // OK.
anatofuz
parents:
diff changeset
176
anatofuz
parents:
diff changeset
177 *p3 = 42; // Warning!
anatofuz
parents:
diff changeset
178 p3.reset(new int); // OK.
anatofuz
parents:
diff changeset
179 }
anatofuz
parents:
diff changeset
180
anatofuz
parents:
diff changeset
181
anatofuz
parents:
diff changeset
182 REQUIRES(...), REQUIRES_SHARED(...)
anatofuz
parents:
diff changeset
183 -----------------------------------
anatofuz
parents:
diff changeset
184
anatofuz
parents:
diff changeset
185 *Previously*: ``EXCLUSIVE_LOCKS_REQUIRED``, ``SHARED_LOCKS_REQUIRED``
anatofuz
parents:
diff changeset
186
anatofuz
parents:
diff changeset
187 ``REQUIRES`` is an attribute on functions or methods, which
anatofuz
parents:
diff changeset
188 declares that the calling thread must have exclusive access to the given
anatofuz
parents:
diff changeset
189 capabilities. More than one capability may be specified. The capabilities
anatofuz
parents:
diff changeset
190 must be held on entry to the function, *and must still be held on exit*.
anatofuz
parents:
diff changeset
191
anatofuz
parents:
diff changeset
192 ``REQUIRES_SHARED`` is similar, but requires only shared access.
anatofuz
parents:
diff changeset
193
anatofuz
parents:
diff changeset
194 .. code-block:: c++
anatofuz
parents:
diff changeset
195
anatofuz
parents:
diff changeset
196 Mutex mu1, mu2;
anatofuz
parents:
diff changeset
197 int a GUARDED_BY(mu1);
anatofuz
parents:
diff changeset
198 int b GUARDED_BY(mu2);
anatofuz
parents:
diff changeset
199
anatofuz
parents:
diff changeset
200 void foo() REQUIRES(mu1, mu2) {
anatofuz
parents:
diff changeset
201 a = 0;
anatofuz
parents:
diff changeset
202 b = 0;
anatofuz
parents:
diff changeset
203 }
anatofuz
parents:
diff changeset
204
anatofuz
parents:
diff changeset
205 void test() {
anatofuz
parents:
diff changeset
206 mu1.Lock();
anatofuz
parents:
diff changeset
207 foo(); // Warning! Requires mu2.
anatofuz
parents:
diff changeset
208 mu1.Unlock();
anatofuz
parents:
diff changeset
209 }
anatofuz
parents:
diff changeset
210
anatofuz
parents:
diff changeset
211
anatofuz
parents:
diff changeset
212 ACQUIRE(...), ACQUIRE_SHARED(...), RELEASE(...), RELEASE_SHARED(...)
anatofuz
parents:
diff changeset
213 --------------------------------------------------------------------
anatofuz
parents:
diff changeset
214
anatofuz
parents:
diff changeset
215 *Previously*: ``EXCLUSIVE_LOCK_FUNCTION``, ``SHARED_LOCK_FUNCTION``,
anatofuz
parents:
diff changeset
216 ``UNLOCK_FUNCTION``
anatofuz
parents:
diff changeset
217
anatofuz
parents:
diff changeset
218 ``ACQUIRE`` is an attribute on functions or methods, which
anatofuz
parents:
diff changeset
219 declares that the function acquires a capability, but does not release it. The
anatofuz
parents:
diff changeset
220 caller must not hold the given capability on entry, and it will hold the
anatofuz
parents:
diff changeset
221 capability on exit. ``ACQUIRE_SHARED`` is similar.
anatofuz
parents:
diff changeset
222
anatofuz
parents:
diff changeset
223 ``RELEASE`` and ``RELEASE_SHARED`` declare that the function releases the given
anatofuz
parents:
diff changeset
224 capability. The caller must hold the capability on entry, and will no longer
anatofuz
parents:
diff changeset
225 hold it on exit. It does not matter whether the given capability is shared or
anatofuz
parents:
diff changeset
226 exclusive.
anatofuz
parents:
diff changeset
227
anatofuz
parents:
diff changeset
228 .. code-block:: c++
anatofuz
parents:
diff changeset
229
anatofuz
parents:
diff changeset
230 Mutex mu;
anatofuz
parents:
diff changeset
231 MyClass myObject GUARDED_BY(mu);
anatofuz
parents:
diff changeset
232
anatofuz
parents:
diff changeset
233 void lockAndInit() ACQUIRE(mu) {
anatofuz
parents:
diff changeset
234 mu.Lock();
anatofuz
parents:
diff changeset
235 myObject.init();
anatofuz
parents:
diff changeset
236 }
anatofuz
parents:
diff changeset
237
anatofuz
parents:
diff changeset
238 void cleanupAndUnlock() RELEASE(mu) {
anatofuz
parents:
diff changeset
239 myObject.cleanup();
anatofuz
parents:
diff changeset
240 } // Warning! Need to unlock mu.
anatofuz
parents:
diff changeset
241
anatofuz
parents:
diff changeset
242 void test() {
anatofuz
parents:
diff changeset
243 lockAndInit();
anatofuz
parents:
diff changeset
244 myObject.doSomething();
anatofuz
parents:
diff changeset
245 cleanupAndUnlock();
anatofuz
parents:
diff changeset
246 myObject.doSomething(); // Warning, mu is not locked.
anatofuz
parents:
diff changeset
247 }
anatofuz
parents:
diff changeset
248
anatofuz
parents:
diff changeset
249 If no argument is passed to ``ACQUIRE`` or ``RELEASE``, then the argument is
anatofuz
parents:
diff changeset
250 assumed to be ``this``, and the analysis will not check the body of the
anatofuz
parents:
diff changeset
251 function. This pattern is intended for use by classes which hide locking
anatofuz
parents:
diff changeset
252 details behind an abstract interface. For example:
anatofuz
parents:
diff changeset
253
anatofuz
parents:
diff changeset
254 .. code-block:: c++
anatofuz
parents:
diff changeset
255
anatofuz
parents:
diff changeset
256 template <class T>
anatofuz
parents:
diff changeset
257 class CAPABILITY("mutex") Container {
anatofuz
parents:
diff changeset
258 private:
anatofuz
parents:
diff changeset
259 Mutex mu;
anatofuz
parents:
diff changeset
260 T* data;
anatofuz
parents:
diff changeset
261
anatofuz
parents:
diff changeset
262 public:
anatofuz
parents:
diff changeset
263 // Hide mu from public interface.
anatofuz
parents:
diff changeset
264 void Lock() ACQUIRE() { mu.Lock(); }
anatofuz
parents:
diff changeset
265 void Unlock() RELEASE() { mu.Unlock(); }
anatofuz
parents:
diff changeset
266
anatofuz
parents:
diff changeset
267 T& getElem(int i) { return data[i]; }
anatofuz
parents:
diff changeset
268 };
anatofuz
parents:
diff changeset
269
anatofuz
parents:
diff changeset
270 void test() {
anatofuz
parents:
diff changeset
271 Container<int> c;
anatofuz
parents:
diff changeset
272 c.Lock();
anatofuz
parents:
diff changeset
273 int i = c.getElem(0);
anatofuz
parents:
diff changeset
274 c.Unlock();
anatofuz
parents:
diff changeset
275 }
anatofuz
parents:
diff changeset
276
anatofuz
parents:
diff changeset
277
anatofuz
parents:
diff changeset
278 EXCLUDES(...)
anatofuz
parents:
diff changeset
279 -------------
anatofuz
parents:
diff changeset
280
anatofuz
parents:
diff changeset
281 *Previously*: ``LOCKS_EXCLUDED``
anatofuz
parents:
diff changeset
282
anatofuz
parents:
diff changeset
283 ``EXCLUDES`` is an attribute on functions or methods, which declares that
anatofuz
parents:
diff changeset
284 the caller must *not* hold the given capabilities. This annotation is
anatofuz
parents:
diff changeset
285 used to prevent deadlock. Many mutex implementations are not re-entrant, so
anatofuz
parents:
diff changeset
286 deadlock can occur if the function acquires the mutex a second time.
anatofuz
parents:
diff changeset
287
anatofuz
parents:
diff changeset
288 .. code-block:: c++
anatofuz
parents:
diff changeset
289
anatofuz
parents:
diff changeset
290 Mutex mu;
anatofuz
parents:
diff changeset
291 int a GUARDED_BY(mu);
anatofuz
parents:
diff changeset
292
anatofuz
parents:
diff changeset
293 void clear() EXCLUDES(mu) {
anatofuz
parents:
diff changeset
294 mu.Lock();
anatofuz
parents:
diff changeset
295 a = 0;
anatofuz
parents:
diff changeset
296 mu.Unlock();
anatofuz
parents:
diff changeset
297 }
anatofuz
parents:
diff changeset
298
anatofuz
parents:
diff changeset
299 void reset() {
anatofuz
parents:
diff changeset
300 mu.Lock();
anatofuz
parents:
diff changeset
301 clear(); // Warning! Caller cannot hold 'mu'.
anatofuz
parents:
diff changeset
302 mu.Unlock();
anatofuz
parents:
diff changeset
303 }
anatofuz
parents:
diff changeset
304
anatofuz
parents:
diff changeset
305 Unlike ``REQUIRES``, ``EXCLUDES`` is optional. The analysis will not issue a
anatofuz
parents:
diff changeset
306 warning if the attribute is missing, which can lead to false negatives in some
anatofuz
parents:
diff changeset
307 cases. This issue is discussed further in :ref:`negative`.
anatofuz
parents:
diff changeset
308
anatofuz
parents:
diff changeset
309
anatofuz
parents:
diff changeset
310 NO_THREAD_SAFETY_ANALYSIS
anatofuz
parents:
diff changeset
311 -------------------------
anatofuz
parents:
diff changeset
312
anatofuz
parents:
diff changeset
313 ``NO_THREAD_SAFETY_ANALYSIS`` is an attribute on functions or methods, which
anatofuz
parents:
diff changeset
314 turns off thread safety checking for that method. It provides an escape hatch
anatofuz
parents:
diff changeset
315 for functions which are either (1) deliberately thread-unsafe, or (2) are
anatofuz
parents:
diff changeset
316 thread-safe, but too complicated for the analysis to understand. Reasons for
anatofuz
parents:
diff changeset
317 (2) will be described in the :ref:`limitations`, below.
anatofuz
parents:
diff changeset
318
anatofuz
parents:
diff changeset
319 .. code-block:: c++
anatofuz
parents:
diff changeset
320
anatofuz
parents:
diff changeset
321 class Counter {
anatofuz
parents:
diff changeset
322 Mutex mu;
anatofuz
parents:
diff changeset
323 int a GUARDED_BY(mu);
anatofuz
parents:
diff changeset
324
anatofuz
parents:
diff changeset
325 void unsafeIncrement() NO_THREAD_SAFETY_ANALYSIS { a++; }
anatofuz
parents:
diff changeset
326 };
anatofuz
parents:
diff changeset
327
anatofuz
parents:
diff changeset
328 Unlike the other attributes, NO_THREAD_SAFETY_ANALYSIS is not part of the
anatofuz
parents:
diff changeset
329 interface of a function, and should thus be placed on the function definition
anatofuz
parents:
diff changeset
330 (in the ``.cc`` or ``.cpp`` file) rather than on the function declaration
anatofuz
parents:
diff changeset
331 (in the header).
anatofuz
parents:
diff changeset
332
anatofuz
parents:
diff changeset
333
anatofuz
parents:
diff changeset
334 RETURN_CAPABILITY(c)
anatofuz
parents:
diff changeset
335 --------------------
anatofuz
parents:
diff changeset
336
anatofuz
parents:
diff changeset
337 *Previously*: ``LOCK_RETURNED``
anatofuz
parents:
diff changeset
338
anatofuz
parents:
diff changeset
339 ``RETURN_CAPABILITY`` is an attribute on functions or methods, which declares
anatofuz
parents:
diff changeset
340 that the function returns a reference to the given capability. It is used to
anatofuz
parents:
diff changeset
341 annotate getter methods that return mutexes.
anatofuz
parents:
diff changeset
342
anatofuz
parents:
diff changeset
343 .. code-block:: c++
anatofuz
parents:
diff changeset
344
anatofuz
parents:
diff changeset
345 class MyClass {
anatofuz
parents:
diff changeset
346 private:
anatofuz
parents:
diff changeset
347 Mutex mu;
anatofuz
parents:
diff changeset
348 int a GUARDED_BY(mu);
anatofuz
parents:
diff changeset
349
anatofuz
parents:
diff changeset
350 public:
anatofuz
parents:
diff changeset
351 Mutex* getMu() RETURN_CAPABILITY(mu) { return &mu; }
anatofuz
parents:
diff changeset
352
anatofuz
parents:
diff changeset
353 // analysis knows that getMu() == mu
anatofuz
parents:
diff changeset
354 void clear() REQUIRES(getMu()) { a = 0; }
anatofuz
parents:
diff changeset
355 };
anatofuz
parents:
diff changeset
356
anatofuz
parents:
diff changeset
357
anatofuz
parents:
diff changeset
358 ACQUIRED_BEFORE(...), ACQUIRED_AFTER(...)
anatofuz
parents:
diff changeset
359 -----------------------------------------
anatofuz
parents:
diff changeset
360
anatofuz
parents:
diff changeset
361 ``ACQUIRED_BEFORE`` and ``ACQUIRED_AFTER`` are attributes on member
anatofuz
parents:
diff changeset
362 declarations, specifically declarations of mutexes or other capabilities.
anatofuz
parents:
diff changeset
363 These declarations enforce a particular order in which the mutexes must be
anatofuz
parents:
diff changeset
364 acquired, in order to prevent deadlock.
anatofuz
parents:
diff changeset
365
anatofuz
parents:
diff changeset
366 .. code-block:: c++
anatofuz
parents:
diff changeset
367
anatofuz
parents:
diff changeset
368 Mutex m1;
anatofuz
parents:
diff changeset
369 Mutex m2 ACQUIRED_AFTER(m1);
anatofuz
parents:
diff changeset
370
anatofuz
parents:
diff changeset
371 // Alternative declaration
anatofuz
parents:
diff changeset
372 // Mutex m2;
anatofuz
parents:
diff changeset
373 // Mutex m1 ACQUIRED_BEFORE(m2);
anatofuz
parents:
diff changeset
374
anatofuz
parents:
diff changeset
375 void foo() {
anatofuz
parents:
diff changeset
376 m2.Lock();
anatofuz
parents:
diff changeset
377 m1.Lock(); // Warning! m2 must be acquired after m1.
anatofuz
parents:
diff changeset
378 m1.Unlock();
anatofuz
parents:
diff changeset
379 m2.Unlock();
anatofuz
parents:
diff changeset
380 }
anatofuz
parents:
diff changeset
381
anatofuz
parents:
diff changeset
382
anatofuz
parents:
diff changeset
383 CAPABILITY(<string>)
anatofuz
parents:
diff changeset
384 --------------------
anatofuz
parents:
diff changeset
385
anatofuz
parents:
diff changeset
386 *Previously*: ``LOCKABLE``
anatofuz
parents:
diff changeset
387
anatofuz
parents:
diff changeset
388 ``CAPABILITY`` is an attribute on classes, which specifies that objects of the
anatofuz
parents:
diff changeset
389 class can be used as a capability. The string argument specifies the kind of
anatofuz
parents:
diff changeset
390 capability in error messages, e.g. ``"mutex"``. See the ``Container`` example
anatofuz
parents:
diff changeset
391 given above, or the ``Mutex`` class in :ref:`mutexheader`.
anatofuz
parents:
diff changeset
392
anatofuz
parents:
diff changeset
393
anatofuz
parents:
diff changeset
394 SCOPED_CAPABILITY
anatofuz
parents:
diff changeset
395 -----------------
anatofuz
parents:
diff changeset
396
anatofuz
parents:
diff changeset
397 *Previously*: ``SCOPED_LOCKABLE``
anatofuz
parents:
diff changeset
398
anatofuz
parents:
diff changeset
399 ``SCOPED_CAPABILITY`` is an attribute on classes that implement RAII-style
anatofuz
parents:
diff changeset
400 locking, in which a capability is acquired in the constructor, and released in
anatofuz
parents:
diff changeset
401 the destructor. Such classes require special handling because the constructor
anatofuz
parents:
diff changeset
402 and destructor refer to the capability via different names; see the
anatofuz
parents:
diff changeset
403 ``MutexLocker`` class in :ref:`mutexheader`, below.
anatofuz
parents:
diff changeset
404
anatofuz
parents:
diff changeset
405
anatofuz
parents:
diff changeset
406 TRY_ACQUIRE(<bool>, ...), TRY_ACQUIRE_SHARED(<bool>, ...)
anatofuz
parents:
diff changeset
407 ---------------------------------------------------------
anatofuz
parents:
diff changeset
408
anatofuz
parents:
diff changeset
409 *Previously:* ``EXCLUSIVE_TRYLOCK_FUNCTION``, ``SHARED_TRYLOCK_FUNCTION``
anatofuz
parents:
diff changeset
410
anatofuz
parents:
diff changeset
411 These are attributes on a function or method that tries to acquire the given
anatofuz
parents:
diff changeset
412 capability, and returns a boolean value indicating success or failure.
anatofuz
parents:
diff changeset
413 The first argument must be ``true`` or ``false``, to specify which return value
anatofuz
parents:
diff changeset
414 indicates success, and the remaining arguments are interpreted in the same way
anatofuz
parents:
diff changeset
415 as ``ACQUIRE``. See :ref:`mutexheader`, below, for example uses.
anatofuz
parents:
diff changeset
416
anatofuz
parents:
diff changeset
417
anatofuz
parents:
diff changeset
418 ASSERT_CAPABILITY(...) and ASSERT_SHARED_CAPABILITY(...)
anatofuz
parents:
diff changeset
419 --------------------------------------------------------
anatofuz
parents:
diff changeset
420
anatofuz
parents:
diff changeset
421 *Previously:* ``ASSERT_EXCLUSIVE_LOCK``, ``ASSERT_SHARED_LOCK``
anatofuz
parents:
diff changeset
422
anatofuz
parents:
diff changeset
423 These are attributes on a function or method that does a run-time test to see
anatofuz
parents:
diff changeset
424 whether the calling thread holds the given capability. The function is assumed
anatofuz
parents:
diff changeset
425 to fail (no return) if the capability is not held. See :ref:`mutexheader`,
anatofuz
parents:
diff changeset
426 below, for example uses.
anatofuz
parents:
diff changeset
427
anatofuz
parents:
diff changeset
428
anatofuz
parents:
diff changeset
429 GUARDED_VAR and PT_GUARDED_VAR
anatofuz
parents:
diff changeset
430 ------------------------------
anatofuz
parents:
diff changeset
431
anatofuz
parents:
diff changeset
432 Use of these attributes has been deprecated.
anatofuz
parents:
diff changeset
433
anatofuz
parents:
diff changeset
434
anatofuz
parents:
diff changeset
435 Warning flags
anatofuz
parents:
diff changeset
436 -------------
anatofuz
parents:
diff changeset
437
anatofuz
parents:
diff changeset
438 * ``-Wthread-safety``: Umbrella flag which turns on the following three:
anatofuz
parents:
diff changeset
439
anatofuz
parents:
diff changeset
440 + ``-Wthread-safety-attributes``: Sanity checks on attribute syntax.
anatofuz
parents:
diff changeset
441 + ``-Wthread-safety-analysis``: The core analysis.
anatofuz
parents:
diff changeset
442 + ``-Wthread-safety-precise``: Requires that mutex expressions match precisely.
anatofuz
parents:
diff changeset
443 This warning can be disabled for code which has a lot of aliases.
anatofuz
parents:
diff changeset
444 + ``-Wthread-safety-reference``: Checks when guarded members are passed by reference.
anatofuz
parents:
diff changeset
445
anatofuz
parents:
diff changeset
446
anatofuz
parents:
diff changeset
447 :ref:`negative` are an experimental feature, which are enabled with:
anatofuz
parents:
diff changeset
448
anatofuz
parents:
diff changeset
449 * ``-Wthread-safety-negative``: Negative capabilities. Off by default.
anatofuz
parents:
diff changeset
450
anatofuz
parents:
diff changeset
451 When new features and checks are added to the analysis, they can often introduce
anatofuz
parents:
diff changeset
452 additional warnings. Those warnings are initially released as *beta* warnings
anatofuz
parents:
diff changeset
453 for a period of time, after which they are migrated into the standard analysis.
anatofuz
parents:
diff changeset
454
anatofuz
parents:
diff changeset
455 * ``-Wthread-safety-beta``: New features. Off by default.
anatofuz
parents:
diff changeset
456
anatofuz
parents:
diff changeset
457
anatofuz
parents:
diff changeset
458 .. _negative:
anatofuz
parents:
diff changeset
459
anatofuz
parents:
diff changeset
460 Negative Capabilities
anatofuz
parents:
diff changeset
461 =====================
anatofuz
parents:
diff changeset
462
anatofuz
parents:
diff changeset
463 Thread Safety Analysis is designed to prevent both race conditions and
anatofuz
parents:
diff changeset
464 deadlock. The GUARDED_BY and REQUIRES attributes prevent race conditions, by
anatofuz
parents:
diff changeset
465 ensuring that a capability is held before reading or writing to guarded data,
anatofuz
parents:
diff changeset
466 and the EXCLUDES attribute prevents deadlock, by making sure that a mutex is
anatofuz
parents:
diff changeset
467 *not* held.
anatofuz
parents:
diff changeset
468
anatofuz
parents:
diff changeset
469 However, EXCLUDES is an optional attribute, and does not provide the same
anatofuz
parents:
diff changeset
470 safety guarantee as REQUIRES. In particular:
anatofuz
parents:
diff changeset
471
anatofuz
parents:
diff changeset
472 * A function which acquires a capability does not have to exclude it.
anatofuz
parents:
diff changeset
473 * A function which calls a function that excludes a capability does not
anatofuz
parents:
diff changeset
474 have transitively exclude that capability.
anatofuz
parents:
diff changeset
475
anatofuz
parents:
diff changeset
476 As a result, EXCLUDES can easily produce false negatives:
anatofuz
parents:
diff changeset
477
anatofuz
parents:
diff changeset
478 .. code-block:: c++
anatofuz
parents:
diff changeset
479
anatofuz
parents:
diff changeset
480 class Foo {
anatofuz
parents:
diff changeset
481 Mutex mu;
anatofuz
parents:
diff changeset
482
anatofuz
parents:
diff changeset
483 void foo() {
anatofuz
parents:
diff changeset
484 mu.Lock();
anatofuz
parents:
diff changeset
485 bar(); // No warning.
anatofuz
parents:
diff changeset
486 baz(); // No warning.
anatofuz
parents:
diff changeset
487 mu.Unlock();
anatofuz
parents:
diff changeset
488 }
anatofuz
parents:
diff changeset
489
anatofuz
parents:
diff changeset
490 void bar() { // No warning. (Should have EXCLUDES(mu)).
anatofuz
parents:
diff changeset
491 mu.Lock();
anatofuz
parents:
diff changeset
492 // ...
anatofuz
parents:
diff changeset
493 mu.Unlock();
anatofuz
parents:
diff changeset
494 }
anatofuz
parents:
diff changeset
495
anatofuz
parents:
diff changeset
496 void baz() {
anatofuz
parents:
diff changeset
497 bif(); // No warning. (Should have EXCLUDES(mu)).
anatofuz
parents:
diff changeset
498 }
anatofuz
parents:
diff changeset
499
anatofuz
parents:
diff changeset
500 void bif() EXCLUDES(mu);
anatofuz
parents:
diff changeset
501 };
anatofuz
parents:
diff changeset
502
anatofuz
parents:
diff changeset
503
anatofuz
parents:
diff changeset
504 Negative requirements are an alternative EXCLUDES that provide
anatofuz
parents:
diff changeset
505 a stronger safety guarantee. A negative requirement uses the REQUIRES
anatofuz
parents:
diff changeset
506 attribute, in conjunction with the ``!`` operator, to indicate that a capability
anatofuz
parents:
diff changeset
507 should *not* be held.
anatofuz
parents:
diff changeset
508
anatofuz
parents:
diff changeset
509 For example, using ``REQUIRES(!mu)`` instead of ``EXCLUDES(mu)`` will produce
anatofuz
parents:
diff changeset
510 the appropriate warnings:
anatofuz
parents:
diff changeset
511
anatofuz
parents:
diff changeset
512 .. code-block:: c++
anatofuz
parents:
diff changeset
513
anatofuz
parents:
diff changeset
514 class FooNeg {
anatofuz
parents:
diff changeset
515 Mutex mu;
anatofuz
parents:
diff changeset
516
anatofuz
parents:
diff changeset
517 void foo() REQUIRES(!mu) { // foo() now requires !mu.
anatofuz
parents:
diff changeset
518 mu.Lock();
anatofuz
parents:
diff changeset
519 bar();
anatofuz
parents:
diff changeset
520 baz();
anatofuz
parents:
diff changeset
521 mu.Unlock();
anatofuz
parents:
diff changeset
522 }
anatofuz
parents:
diff changeset
523
anatofuz
parents:
diff changeset
524 void bar() {
anatofuz
parents:
diff changeset
525 mu.Lock(); // WARNING! Missing REQUIRES(!mu).
anatofuz
parents:
diff changeset
526 // ...
anatofuz
parents:
diff changeset
527 mu.Unlock();
anatofuz
parents:
diff changeset
528 }
anatofuz
parents:
diff changeset
529
anatofuz
parents:
diff changeset
530 void baz() {
anatofuz
parents:
diff changeset
531 bif(); // WARNING! Missing REQUIRES(!mu).
anatofuz
parents:
diff changeset
532 }
anatofuz
parents:
diff changeset
533
anatofuz
parents:
diff changeset
534 void bif() REQUIRES(!mu);
anatofuz
parents:
diff changeset
535 };
anatofuz
parents:
diff changeset
536
anatofuz
parents:
diff changeset
537
anatofuz
parents:
diff changeset
538 Negative requirements are an experimental feature which is off by default,
anatofuz
parents:
diff changeset
539 because it will produce many warnings in existing code. It can be enabled
anatofuz
parents:
diff changeset
540 by passing ``-Wthread-safety-negative``.
anatofuz
parents:
diff changeset
541
anatofuz
parents:
diff changeset
542
anatofuz
parents:
diff changeset
543 .. _faq:
anatofuz
parents:
diff changeset
544
anatofuz
parents:
diff changeset
545 Frequently Asked Questions
anatofuz
parents:
diff changeset
546 ==========================
anatofuz
parents:
diff changeset
547
anatofuz
parents:
diff changeset
548 (Q) Should I put attributes in the header file, or in the .cc/.cpp/.cxx file?
anatofuz
parents:
diff changeset
549
anatofuz
parents:
diff changeset
550 (A) Attributes are part of the formal interface of a function, and should
anatofuz
parents:
diff changeset
551 always go in the header, where they are visible to anything that includes
anatofuz
parents:
diff changeset
552 the header. Attributes in the .cpp file are not visible outside of the
anatofuz
parents:
diff changeset
553 immediate translation unit, which leads to false negatives and false positives.
anatofuz
parents:
diff changeset
554
anatofuz
parents:
diff changeset
555
anatofuz
parents:
diff changeset
556 (Q) "*Mutex is not locked on every path through here?*" What does that mean?
anatofuz
parents:
diff changeset
557
anatofuz
parents:
diff changeset
558 (A) See :ref:`conditional_locks`, below.
anatofuz
parents:
diff changeset
559
anatofuz
parents:
diff changeset
560
anatofuz
parents:
diff changeset
561 .. _limitations:
anatofuz
parents:
diff changeset
562
anatofuz
parents:
diff changeset
563 Known Limitations
anatofuz
parents:
diff changeset
564 =================
anatofuz
parents:
diff changeset
565
anatofuz
parents:
diff changeset
566 Lexical scope
anatofuz
parents:
diff changeset
567 -------------
anatofuz
parents:
diff changeset
568
anatofuz
parents:
diff changeset
569 Thread safety attributes contain ordinary C++ expressions, and thus follow
anatofuz
parents:
diff changeset
570 ordinary C++ scoping rules. In particular, this means that mutexes and other
anatofuz
parents:
diff changeset
571 capabilities must be declared before they can be used in an attribute.
anatofuz
parents:
diff changeset
572 Use-before-declaration is okay within a single class, because attributes are
anatofuz
parents:
diff changeset
573 parsed at the same time as method bodies. (C++ delays parsing of method bodies
anatofuz
parents:
diff changeset
574 until the end of the class.) However, use-before-declaration is not allowed
anatofuz
parents:
diff changeset
575 between classes, as illustrated below.
anatofuz
parents:
diff changeset
576
anatofuz
parents:
diff changeset
577 .. code-block:: c++
anatofuz
parents:
diff changeset
578
anatofuz
parents:
diff changeset
579 class Foo;
anatofuz
parents:
diff changeset
580
anatofuz
parents:
diff changeset
581 class Bar {
anatofuz
parents:
diff changeset
582 void bar(Foo* f) REQUIRES(f->mu); // Error: mu undeclared.
anatofuz
parents:
diff changeset
583 };
anatofuz
parents:
diff changeset
584
anatofuz
parents:
diff changeset
585 class Foo {
anatofuz
parents:
diff changeset
586 Mutex mu;
anatofuz
parents:
diff changeset
587 };
anatofuz
parents:
diff changeset
588
anatofuz
parents:
diff changeset
589
anatofuz
parents:
diff changeset
590 Private Mutexes
anatofuz
parents:
diff changeset
591 ---------------
anatofuz
parents:
diff changeset
592
anatofuz
parents:
diff changeset
593 Good software engineering practice dictates that mutexes should be private
anatofuz
parents:
diff changeset
594 members, because the locking mechanism used by a thread-safe class is part of
anatofuz
parents:
diff changeset
595 its internal implementation. However, private mutexes can sometimes leak into
anatofuz
parents:
diff changeset
596 the public interface of a class.
anatofuz
parents:
diff changeset
597 Thread safety attributes follow normal C++ access restrictions, so if ``mu``
anatofuz
parents:
diff changeset
598 is a private member of ``c``, then it is an error to write ``c.mu`` in an
anatofuz
parents:
diff changeset
599 attribute.
anatofuz
parents:
diff changeset
600
anatofuz
parents:
diff changeset
601 One workaround is to (ab)use the ``RETURN_CAPABILITY`` attribute to provide a
anatofuz
parents:
diff changeset
602 public *name* for a private mutex, without actually exposing the underlying
anatofuz
parents:
diff changeset
603 mutex. For example:
anatofuz
parents:
diff changeset
604
anatofuz
parents:
diff changeset
605 .. code-block:: c++
anatofuz
parents:
diff changeset
606
anatofuz
parents:
diff changeset
607 class MyClass {
anatofuz
parents:
diff changeset
608 private:
anatofuz
parents:
diff changeset
609 Mutex mu;
anatofuz
parents:
diff changeset
610
anatofuz
parents:
diff changeset
611 public:
anatofuz
parents:
diff changeset
612 // For thread safety analysis only. Does not actually return mu.
anatofuz
parents:
diff changeset
613 Mutex* getMu() RETURN_CAPABILITY(mu) { return 0; }
anatofuz
parents:
diff changeset
614
anatofuz
parents:
diff changeset
615 void doSomething() REQUIRES(mu);
anatofuz
parents:
diff changeset
616 };
anatofuz
parents:
diff changeset
617
anatofuz
parents:
diff changeset
618 void doSomethingTwice(MyClass& c) REQUIRES(c.getMu()) {
anatofuz
parents:
diff changeset
619 // The analysis thinks that c.getMu() == c.mu
anatofuz
parents:
diff changeset
620 c.doSomething();
anatofuz
parents:
diff changeset
621 c.doSomething();
anatofuz
parents:
diff changeset
622 }
anatofuz
parents:
diff changeset
623
anatofuz
parents:
diff changeset
624 In the above example, ``doSomethingTwice()`` is an external routine that
anatofuz
parents:
diff changeset
625 requires ``c.mu`` to be locked, which cannot be declared directly because ``mu``
anatofuz
parents:
diff changeset
626 is private. This pattern is discouraged because it
anatofuz
parents:
diff changeset
627 violates encapsulation, but it is sometimes necessary, especially when adding
anatofuz
parents:
diff changeset
628 annotations to an existing code base. The workaround is to define ``getMu()``
anatofuz
parents:
diff changeset
629 as a fake getter method, which is provided only for the benefit of thread
anatofuz
parents:
diff changeset
630 safety analysis.
anatofuz
parents:
diff changeset
631
anatofuz
parents:
diff changeset
632
anatofuz
parents:
diff changeset
633 .. _conditional_locks:
anatofuz
parents:
diff changeset
634
anatofuz
parents:
diff changeset
635 No conditionally held locks.
anatofuz
parents:
diff changeset
636 ----------------------------
anatofuz
parents:
diff changeset
637
anatofuz
parents:
diff changeset
638 The analysis must be able to determine whether a lock is held, or not held, at
anatofuz
parents:
diff changeset
639 every program point. Thus, sections of code where a lock *might be held* will
anatofuz
parents:
diff changeset
640 generate spurious warnings (false positives). For example:
anatofuz
parents:
diff changeset
641
anatofuz
parents:
diff changeset
642 .. code-block:: c++
anatofuz
parents:
diff changeset
643
anatofuz
parents:
diff changeset
644 void foo() {
anatofuz
parents:
diff changeset
645 bool b = needsToLock();
anatofuz
parents:
diff changeset
646 if (b) mu.Lock();
anatofuz
parents:
diff changeset
647 ... // Warning! Mutex 'mu' is not held on every path through here.
anatofuz
parents:
diff changeset
648 if (b) mu.Unlock();
anatofuz
parents:
diff changeset
649 }
anatofuz
parents:
diff changeset
650
anatofuz
parents:
diff changeset
651
anatofuz
parents:
diff changeset
652 No checking inside constructors and destructors.
anatofuz
parents:
diff changeset
653 ------------------------------------------------
anatofuz
parents:
diff changeset
654
anatofuz
parents:
diff changeset
655 The analysis currently does not do any checking inside constructors or
anatofuz
parents:
diff changeset
656 destructors. In other words, every constructor and destructor is treated as
anatofuz
parents:
diff changeset
657 if it was annotated with ``NO_THREAD_SAFETY_ANALYSIS``.
anatofuz
parents:
diff changeset
658 The reason for this is that during initialization, only one thread typically
anatofuz
parents:
diff changeset
659 has access to the object which is being initialized, and it is thus safe (and
anatofuz
parents:
diff changeset
660 common practice) to initialize guarded members without acquiring any locks.
anatofuz
parents:
diff changeset
661 The same is true of destructors.
anatofuz
parents:
diff changeset
662
anatofuz
parents:
diff changeset
663 Ideally, the analysis would allow initialization of guarded members inside the
anatofuz
parents:
diff changeset
664 object being initialized or destroyed, while still enforcing the usual access
anatofuz
parents:
diff changeset
665 restrictions on everything else. However, this is difficult to enforce in
anatofuz
parents:
diff changeset
666 practice, because in complex pointer-based data structures, it is hard to
anatofuz
parents:
diff changeset
667 determine what data is owned by the enclosing object.
anatofuz
parents:
diff changeset
668
anatofuz
parents:
diff changeset
669 No inlining.
anatofuz
parents:
diff changeset
670 ------------
anatofuz
parents:
diff changeset
671
anatofuz
parents:
diff changeset
672 Thread safety analysis is strictly intra-procedural, just like ordinary type
anatofuz
parents:
diff changeset
673 checking. It relies only on the declared attributes of a function, and will
anatofuz
parents:
diff changeset
674 not attempt to inline any method calls. As a result, code such as the
anatofuz
parents:
diff changeset
675 following will not work:
anatofuz
parents:
diff changeset
676
anatofuz
parents:
diff changeset
677 .. code-block:: c++
anatofuz
parents:
diff changeset
678
anatofuz
parents:
diff changeset
679 template<class T>
anatofuz
parents:
diff changeset
680 class AutoCleanup {
anatofuz
parents:
diff changeset
681 T* object;
anatofuz
parents:
diff changeset
682 void (T::*mp)();
anatofuz
parents:
diff changeset
683
anatofuz
parents:
diff changeset
684 public:
anatofuz
parents:
diff changeset
685 AutoCleanup(T* obj, void (T::*imp)()) : object(obj), mp(imp) { }
anatofuz
parents:
diff changeset
686 ~AutoCleanup() { (object->*mp)(); }
anatofuz
parents:
diff changeset
687 };
anatofuz
parents:
diff changeset
688
anatofuz
parents:
diff changeset
689 Mutex mu;
anatofuz
parents:
diff changeset
690 void foo() {
anatofuz
parents:
diff changeset
691 mu.Lock();
anatofuz
parents:
diff changeset
692 AutoCleanup<Mutex>(&mu, &Mutex::Unlock);
anatofuz
parents:
diff changeset
693 // ...
anatofuz
parents:
diff changeset
694 } // Warning, mu is not unlocked.
anatofuz
parents:
diff changeset
695
anatofuz
parents:
diff changeset
696 In this case, the destructor of ``Autocleanup`` calls ``mu.Unlock()``, so
anatofuz
parents:
diff changeset
697 the warning is bogus. However,
anatofuz
parents:
diff changeset
698 thread safety analysis cannot see the unlock, because it does not attempt to
anatofuz
parents:
diff changeset
699 inline the destructor. Moreover, there is no way to annotate the destructor,
anatofuz
parents:
diff changeset
700 because the destructor is calling a function that is not statically known.
anatofuz
parents:
diff changeset
701 This pattern is simply not supported.
anatofuz
parents:
diff changeset
702
anatofuz
parents:
diff changeset
703
anatofuz
parents:
diff changeset
704 No alias analysis.
anatofuz
parents:
diff changeset
705 ------------------
anatofuz
parents:
diff changeset
706
anatofuz
parents:
diff changeset
707 The analysis currently does not track pointer aliases. Thus, there can be
anatofuz
parents:
diff changeset
708 false positives if two pointers both point to the same mutex.
anatofuz
parents:
diff changeset
709
anatofuz
parents:
diff changeset
710
anatofuz
parents:
diff changeset
711 .. code-block:: c++
anatofuz
parents:
diff changeset
712
anatofuz
parents:
diff changeset
713 class MutexUnlocker {
anatofuz
parents:
diff changeset
714 Mutex* mu;
anatofuz
parents:
diff changeset
715
anatofuz
parents:
diff changeset
716 public:
anatofuz
parents:
diff changeset
717 MutexUnlocker(Mutex* m) RELEASE(m) : mu(m) { mu->Unlock(); }
anatofuz
parents:
diff changeset
718 ~MutexUnlocker() ACQUIRE(mu) { mu->Lock(); }
anatofuz
parents:
diff changeset
719 };
anatofuz
parents:
diff changeset
720
anatofuz
parents:
diff changeset
721 Mutex mutex;
anatofuz
parents:
diff changeset
722 void test() REQUIRES(mutex) {
anatofuz
parents:
diff changeset
723 {
anatofuz
parents:
diff changeset
724 MutexUnlocker munl(&mutex); // unlocks mutex
anatofuz
parents:
diff changeset
725 doSomeIO();
anatofuz
parents:
diff changeset
726 } // Warning: locks munl.mu
anatofuz
parents:
diff changeset
727 }
anatofuz
parents:
diff changeset
728
anatofuz
parents:
diff changeset
729 The MutexUnlocker class is intended to be the dual of the MutexLocker class,
anatofuz
parents:
diff changeset
730 defined in :ref:`mutexheader`. However, it doesn't work because the analysis
anatofuz
parents:
diff changeset
731 doesn't know that munl.mu == mutex. The SCOPED_CAPABILITY attribute handles
anatofuz
parents:
diff changeset
732 aliasing for MutexLocker, but does so only for that particular pattern.
anatofuz
parents:
diff changeset
733
anatofuz
parents:
diff changeset
734
anatofuz
parents:
diff changeset
735 ACQUIRED_BEFORE(...) and ACQUIRED_AFTER(...) are currently unimplemented.
anatofuz
parents:
diff changeset
736 -------------------------------------------------------------------------
anatofuz
parents:
diff changeset
737
anatofuz
parents:
diff changeset
738 To be fixed in a future update.
anatofuz
parents:
diff changeset
739
anatofuz
parents:
diff changeset
740
anatofuz
parents:
diff changeset
741 .. _mutexheader:
anatofuz
parents:
diff changeset
742
anatofuz
parents:
diff changeset
743 mutex.h
anatofuz
parents:
diff changeset
744 =======
anatofuz
parents:
diff changeset
745
anatofuz
parents:
diff changeset
746 Thread safety analysis can be used with any threading library, but it does
anatofuz
parents:
diff changeset
747 require that the threading API be wrapped in classes and methods which have the
anatofuz
parents:
diff changeset
748 appropriate annotations. The following code provides ``mutex.h`` as an example;
anatofuz
parents:
diff changeset
749 these methods should be filled in to call the appropriate underlying
anatofuz
parents:
diff changeset
750 implementation.
anatofuz
parents:
diff changeset
751
anatofuz
parents:
diff changeset
752
anatofuz
parents:
diff changeset
753 .. code-block:: c++
anatofuz
parents:
diff changeset
754
anatofuz
parents:
diff changeset
755
anatofuz
parents:
diff changeset
756 #ifndef THREAD_SAFETY_ANALYSIS_MUTEX_H
anatofuz
parents:
diff changeset
757 #define THREAD_SAFETY_ANALYSIS_MUTEX_H
anatofuz
parents:
diff changeset
758
anatofuz
parents:
diff changeset
759 // Enable thread safety attributes only with clang.
anatofuz
parents:
diff changeset
760 // The attributes can be safely erased when compiling with other compilers.
anatofuz
parents:
diff changeset
761 #if defined(__clang__) && (!defined(SWIG))
anatofuz
parents:
diff changeset
762 #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
anatofuz
parents:
diff changeset
763 #else
anatofuz
parents:
diff changeset
764 #define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
anatofuz
parents:
diff changeset
765 #endif
anatofuz
parents:
diff changeset
766
anatofuz
parents:
diff changeset
767 #define CAPABILITY(x) \
anatofuz
parents:
diff changeset
768 THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
anatofuz
parents:
diff changeset
769
anatofuz
parents:
diff changeset
770 #define SCOPED_CAPABILITY \
anatofuz
parents:
diff changeset
771 THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
anatofuz
parents:
diff changeset
772
anatofuz
parents:
diff changeset
773 #define GUARDED_BY(x) \
anatofuz
parents:
diff changeset
774 THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
anatofuz
parents:
diff changeset
775
anatofuz
parents:
diff changeset
776 #define PT_GUARDED_BY(x) \
anatofuz
parents:
diff changeset
777 THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
anatofuz
parents:
diff changeset
778
anatofuz
parents:
diff changeset
779 #define ACQUIRED_BEFORE(...) \
anatofuz
parents:
diff changeset
780 THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
anatofuz
parents:
diff changeset
781
anatofuz
parents:
diff changeset
782 #define ACQUIRED_AFTER(...) \
anatofuz
parents:
diff changeset
783 THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
anatofuz
parents:
diff changeset
784
anatofuz
parents:
diff changeset
785 #define REQUIRES(...) \
anatofuz
parents:
diff changeset
786 THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
anatofuz
parents:
diff changeset
787
anatofuz
parents:
diff changeset
788 #define REQUIRES_SHARED(...) \
anatofuz
parents:
diff changeset
789 THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
anatofuz
parents:
diff changeset
790
anatofuz
parents:
diff changeset
791 #define ACQUIRE(...) \
anatofuz
parents:
diff changeset
792 THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
anatofuz
parents:
diff changeset
793
anatofuz
parents:
diff changeset
794 #define ACQUIRE_SHARED(...) \
anatofuz
parents:
diff changeset
795 THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
anatofuz
parents:
diff changeset
796
anatofuz
parents:
diff changeset
797 #define RELEASE(...) \
anatofuz
parents:
diff changeset
798 THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
anatofuz
parents:
diff changeset
799
anatofuz
parents:
diff changeset
800 #define RELEASE_SHARED(...) \
anatofuz
parents:
diff changeset
801 THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
anatofuz
parents:
diff changeset
802
anatofuz
parents:
diff changeset
803 #define TRY_ACQUIRE(...) \
anatofuz
parents:
diff changeset
804 THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
anatofuz
parents:
diff changeset
805
anatofuz
parents:
diff changeset
806 #define TRY_ACQUIRE_SHARED(...) \
anatofuz
parents:
diff changeset
807 THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
anatofuz
parents:
diff changeset
808
anatofuz
parents:
diff changeset
809 #define EXCLUDES(...) \
anatofuz
parents:
diff changeset
810 THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
anatofuz
parents:
diff changeset
811
anatofuz
parents:
diff changeset
812 #define ASSERT_CAPABILITY(x) \
anatofuz
parents:
diff changeset
813 THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
anatofuz
parents:
diff changeset
814
anatofuz
parents:
diff changeset
815 #define ASSERT_SHARED_CAPABILITY(x) \
anatofuz
parents:
diff changeset
816 THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
anatofuz
parents:
diff changeset
817
anatofuz
parents:
diff changeset
818 #define RETURN_CAPABILITY(x) \
anatofuz
parents:
diff changeset
819 THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
anatofuz
parents:
diff changeset
820
anatofuz
parents:
diff changeset
821 #define NO_THREAD_SAFETY_ANALYSIS \
anatofuz
parents:
diff changeset
822 THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
anatofuz
parents:
diff changeset
823
anatofuz
parents:
diff changeset
824
anatofuz
parents:
diff changeset
825 // Defines an annotated interface for mutexes.
anatofuz
parents:
diff changeset
826 // These methods can be implemented to use any internal mutex implementation.
anatofuz
parents:
diff changeset
827 class CAPABILITY("mutex") Mutex {
anatofuz
parents:
diff changeset
828 public:
anatofuz
parents:
diff changeset
829 // Acquire/lock this mutex exclusively. Only one thread can have exclusive
anatofuz
parents:
diff changeset
830 // access at any one time. Write operations to guarded data require an
anatofuz
parents:
diff changeset
831 // exclusive lock.
anatofuz
parents:
diff changeset
832 void Lock() ACQUIRE();
anatofuz
parents:
diff changeset
833
anatofuz
parents:
diff changeset
834 // Acquire/lock this mutex for read operations, which require only a shared
anatofuz
parents:
diff changeset
835 // lock. This assumes a multiple-reader, single writer semantics. Multiple
anatofuz
parents:
diff changeset
836 // threads may acquire the mutex simultaneously as readers, but a writer
anatofuz
parents:
diff changeset
837 // must wait for all of them to release the mutex before it can acquire it
anatofuz
parents:
diff changeset
838 // exclusively.
anatofuz
parents:
diff changeset
839 void ReaderLock() ACQUIRE_SHARED();
anatofuz
parents:
diff changeset
840
anatofuz
parents:
diff changeset
841 // Release/unlock an exclusive mutex.
anatofuz
parents:
diff changeset
842 void Unlock() RELEASE();
anatofuz
parents:
diff changeset
843
anatofuz
parents:
diff changeset
844 // Release/unlock a shared mutex.
anatofuz
parents:
diff changeset
845 void ReaderUnlock() RELEASE_SHARED();
anatofuz
parents:
diff changeset
846
anatofuz
parents:
diff changeset
847 // Try to acquire the mutex. Returns true on success, and false on failure.
anatofuz
parents:
diff changeset
848 bool TryLock() TRY_ACQUIRE(true);
anatofuz
parents:
diff changeset
849
anatofuz
parents:
diff changeset
850 // Try to acquire the mutex for read operations.
anatofuz
parents:
diff changeset
851 bool ReaderTryLock() TRY_ACQUIRE_SHARED(true);
anatofuz
parents:
diff changeset
852
anatofuz
parents:
diff changeset
853 // Assert that this mutex is currently held by the calling thread.
anatofuz
parents:
diff changeset
854 void AssertHeld() ASSERT_CAPABILITY(this);
anatofuz
parents:
diff changeset
855
anatofuz
parents:
diff changeset
856 // Assert that is mutex is currently held for read operations.
anatofuz
parents:
diff changeset
857 void AssertReaderHeld() ASSERT_SHARED_CAPABILITY(this);
anatofuz
parents:
diff changeset
858
anatofuz
parents:
diff changeset
859 // For negative capabilities.
anatofuz
parents:
diff changeset
860 const Mutex& operator!() const { return *this; }
anatofuz
parents:
diff changeset
861 };
anatofuz
parents:
diff changeset
862
anatofuz
parents:
diff changeset
863
anatofuz
parents:
diff changeset
864 // MutexLocker is an RAII class that acquires a mutex in its constructor, and
anatofuz
parents:
diff changeset
865 // releases it in its destructor.
anatofuz
parents:
diff changeset
866 class SCOPED_CAPABILITY MutexLocker {
anatofuz
parents:
diff changeset
867 private:
anatofuz
parents:
diff changeset
868 Mutex* mut;
anatofuz
parents:
diff changeset
869
anatofuz
parents:
diff changeset
870 public:
anatofuz
parents:
diff changeset
871 MutexLocker(Mutex *mu) ACQUIRE(mu) : mut(mu) {
anatofuz
parents:
diff changeset
872 mu->Lock();
anatofuz
parents:
diff changeset
873 }
anatofuz
parents:
diff changeset
874 ~MutexLocker() RELEASE() {
anatofuz
parents:
diff changeset
875 mut->Unlock();
anatofuz
parents:
diff changeset
876 }
anatofuz
parents:
diff changeset
877 };
anatofuz
parents:
diff changeset
878
anatofuz
parents:
diff changeset
879
anatofuz
parents:
diff changeset
880 #ifdef USE_LOCK_STYLE_THREAD_SAFETY_ATTRIBUTES
anatofuz
parents:
diff changeset
881 // The original version of thread safety analysis the following attribute
anatofuz
parents:
diff changeset
882 // definitions. These use a lock-based terminology. They are still in use
anatofuz
parents:
diff changeset
883 // by existing thread safety code, and will continue to be supported.
anatofuz
parents:
diff changeset
884
anatofuz
parents:
diff changeset
885 // Deprecated.
anatofuz
parents:
diff changeset
886 #define PT_GUARDED_VAR \
anatofuz
parents:
diff changeset
887 THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_var)
anatofuz
parents:
diff changeset
888
anatofuz
parents:
diff changeset
889 // Deprecated.
anatofuz
parents:
diff changeset
890 #define GUARDED_VAR \
anatofuz
parents:
diff changeset
891 THREAD_ANNOTATION_ATTRIBUTE__(guarded_var)
anatofuz
parents:
diff changeset
892
anatofuz
parents:
diff changeset
893 // Replaced by REQUIRES
anatofuz
parents:
diff changeset
894 #define EXCLUSIVE_LOCKS_REQUIRED(...) \
anatofuz
parents:
diff changeset
895 THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
anatofuz
parents:
diff changeset
896
anatofuz
parents:
diff changeset
897 // Replaced by REQUIRES_SHARED
anatofuz
parents:
diff changeset
898 #define SHARED_LOCKS_REQUIRED(...) \
anatofuz
parents:
diff changeset
899 THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
anatofuz
parents:
diff changeset
900
anatofuz
parents:
diff changeset
901 // Replaced by CAPABILITY
anatofuz
parents:
diff changeset
902 #define LOCKABLE \
anatofuz
parents:
diff changeset
903 THREAD_ANNOTATION_ATTRIBUTE__(lockable)
anatofuz
parents:
diff changeset
904
anatofuz
parents:
diff changeset
905 // Replaced by SCOPED_CAPABILITY
anatofuz
parents:
diff changeset
906 #define SCOPED_LOCKABLE \
anatofuz
parents:
diff changeset
907 THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
anatofuz
parents:
diff changeset
908
anatofuz
parents:
diff changeset
909 // Replaced by ACQUIRE
anatofuz
parents:
diff changeset
910 #define EXCLUSIVE_LOCK_FUNCTION(...) \
anatofuz
parents:
diff changeset
911 THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
anatofuz
parents:
diff changeset
912
anatofuz
parents:
diff changeset
913 // Replaced by ACQUIRE_SHARED
anatofuz
parents:
diff changeset
914 #define SHARED_LOCK_FUNCTION(...) \
anatofuz
parents:
diff changeset
915 THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
anatofuz
parents:
diff changeset
916
anatofuz
parents:
diff changeset
917 // Replaced by RELEASE and RELEASE_SHARED
anatofuz
parents:
diff changeset
918 #define UNLOCK_FUNCTION(...) \
anatofuz
parents:
diff changeset
919 THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
anatofuz
parents:
diff changeset
920
anatofuz
parents:
diff changeset
921 // Replaced by TRY_ACQUIRE
anatofuz
parents:
diff changeset
922 #define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
anatofuz
parents:
diff changeset
923 THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
anatofuz
parents:
diff changeset
924
anatofuz
parents:
diff changeset
925 // Replaced by TRY_ACQUIRE_SHARED
anatofuz
parents:
diff changeset
926 #define SHARED_TRYLOCK_FUNCTION(...) \
anatofuz
parents:
diff changeset
927 THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
anatofuz
parents:
diff changeset
928
anatofuz
parents:
diff changeset
929 // Replaced by ASSERT_CAPABILITY
anatofuz
parents:
diff changeset
930 #define ASSERT_EXCLUSIVE_LOCK(...) \
anatofuz
parents:
diff changeset
931 THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
anatofuz
parents:
diff changeset
932
anatofuz
parents:
diff changeset
933 // Replaced by ASSERT_SHARED_CAPABILITY
anatofuz
parents:
diff changeset
934 #define ASSERT_SHARED_LOCK(...) \
anatofuz
parents:
diff changeset
935 THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__))
anatofuz
parents:
diff changeset
936
anatofuz
parents:
diff changeset
937 // Replaced by EXCLUDE_CAPABILITY.
anatofuz
parents:
diff changeset
938 #define LOCKS_EXCLUDED(...) \
anatofuz
parents:
diff changeset
939 THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
anatofuz
parents:
diff changeset
940
anatofuz
parents:
diff changeset
941 // Replaced by RETURN_CAPABILITY
anatofuz
parents:
diff changeset
942 #define LOCK_RETURNED(x) \
anatofuz
parents:
diff changeset
943 THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
anatofuz
parents:
diff changeset
944
anatofuz
parents:
diff changeset
945 #endif // USE_LOCK_STYLE_THREAD_SAFETY_ATTRIBUTES
anatofuz
parents:
diff changeset
946
anatofuz
parents:
diff changeset
947 #endif // THREAD_SAFETY_ANALYSIS_MUTEX_H
anatofuz
parents:
diff changeset
948