Clang 12 documentation
Clang 12 Documentation includes a range of tools such as AddressSanitizer, ThreadSanitizer, LeakSanitizer, LibTooling, etc.
- The clang AddressSanitizer
- The clang MemorySanitizer
- The clang LeakSanitizer
- The clang UndefinedBehaviorSanitizer
- The clang of the Hardware – assisted – AddressSanitizer
- The clang SafeStack
- The clang ShadowCallStack
- The clang ThreadSanitizer
- The clang of the Thread – Safety – Analysis
- The clang DataFlowSanitizer
This section is a translation of clang documentation Clang 12 Documentation Thread Safety Analysis. For reference only.
introduce
Clang’s thread-safety analysis is a C++ extension that warns of potential race conditions in code. The analysis is completely static (that is, at compile time) and there is no runtime performance cost. This analysis is currently still in development, but is mature enough to be deployed for industrial use. It was developed by Google in collaboration with CERT/SEI and is already widely used in Google’s internal code.
Thread-safety analysis works much like a type system in a multithreaded program. In addition to declaring the type of data (such as int, float, etc.), developers can optionally declare how access to that data is controlled in a multithreaded environment. For example, if Foo is guarded by mu, a mutex, the profiler will throw a warning whenever code reads or writes to Foo without using MU to lock it. Similarly, if there are certain functions that can only be called in a GUI thread, the profiler will generate a warning if those functions are called in another thread.
Begin to use
#include "mutex.h"
class BankAccount {
private:
Mutex mu;
int balance GUARDED_BY(mu);
void depositImpl(int amount) {
balance += amount; // WARNING! Cannot write balance without locking mu.
}
void withdrawImpl(int amount) REQUIRES(mu) {
balance -= amount; // OK. Caller must have locked mu.
}
public:
void withdraw(int amount) {
mu.Lock();
withdrawImpl(amount); // OK. We've locked mu.
} // WARNING! Failed to unlock mu.
void transferFrom(BankAccount& b, int amount) {
mu.Lock();
b.withdrawImpl(amount); // WARNING! Calling withdrawImpl() requires locking b.mu.
depositImpl(amount); // OK. depositImpl() has no requirements.mu.Unlock(); }};Copy the code
This example demonstrates the basic concepts of thread-safety analysis. The GUARDED_BY compiler property declares that before a thread can read or write balance, it must lock the mutex MU to ensure that addition and subtraction to balance are atomic. Similarly, REQUIRES declares: A thread must lock the mutex MU before calling a Client IMPl function. Because the caller is already locked, it is safe to modify Balance inside the function.
The depositImpl() function does not require, so the profiler reports a warning. Thread-safety analysis is not inter-procedural, so requests from callers must be explicitly addressed. There is also a warning in the transferFrom() function because although the method uses this->mu locking, it does not use B.Mu locking. The profiler understands that there are two separate mutex for two different objects.
Finally, there is a warning in the withdraw() function because the mutex MU has not been unlocked. Every lock operation must have a corresponding unlock operation, and the profiler can detect both double locks and double unlocks. A function can ACQUIRE a lock without releasing it (or Vice Versa), but must add the corresponding tag (e.g., using ACQUIRE/RELEASE).
Run the analysis
Set the compiler tag -wthread-safety to run the analysis:
clang -c -Wthread-safety example.cpp
Copy the code
Note: This example assumes the existence of a mutex.h with appropriate annotations, which declares which methods are used to perform operations such as locking, unlocking, and so on.
Basic concept: Ability
By Capability, I mean a thread-safety related Capability, such as a mutex.
The thread-safety analyzer provides a way to use its capability model to protect resources. The resource can be a data member or a function or method that provides access to some potential resource. The profiler ensures that the calling thread cannot access the resource without obtaining the specified capabilities, such as calling a function, or reading or writing data.
These capability models are associated with specific objects in C++ to declare special methods for obtaining and releasing specified capabilities. The names of the objects are used to distinguish capabilities. The most common example is the mutex lock. For example, if MU is a mutex, then calling mu.lock () causes the calling thread to acquire the ability to access data protected by MU. Similarly, calling mu.unlock () releases the capability accordingly.
A thread can acquire this capability in a way that is mutually exclusive or shared. A mutually exclusive capability can only be acquired by one thread at a time, while a shared capability can be acquired by multiple threads at a time. This mechanism can implement a multi-read single-write access pattern. Write operations on protected data require mutually exclusive access, while read operations require shared access only.
At any given point in the program’s execution, a thread can acquire certain capabilities (such as a set of locked mutex locks). These are represented by some running thread accessing a key or token for a given resource. Just like a security key in the physical world, a thread cannot replicate this ability, nor can it destroy it. A thread can only release the capability for use by another thread, or obtain the capability from another thread. Tagging content is independent of the exact mechanism by which capabilities are acquired and released. It assumes that the underlying implementation, such as the mutex implementation, will handle the transfer of this capability in an appropriate manner.
These capabilities, acquired by a given thread at a given point in time when the program is running, are actually the concept of Runtime. Static analysis works by computing approximations of these capability sets, also known as capability environments. The capability environment is calculated for each program point and describes the set of capabilities statically held or not held at a particular point. This environment is a conservative approximation of the total set of capabilities actually held by the thread at Runtime time.
Reference guide
The thread-safety analyzer uses compiler properties to declare the limits of multithreading. These properties must be combined with named declarations such as classes, methods, and data members. It is highly recommended that you use macro definitions for a large number of different compiler properties. You can see the sample definitions below in mutex.h. These macro definitions are used by default throughout the rest of the article.
For historical reasons, the thread-safe preferred version uses the very obvious lock-centric macro definition naming. These macro definitions have been renamed to fit a more general capability model. These preferred names are still in use, and tags are clearly indicated in appropriate scenarios.
And PT_GUARDED_BY GUARDED_BY (c) (c)
GUARDED_BY is an attribute for a data member that declares that the data member is protected by a given capability. Read data requires shared access, while write data requires mutually exclusive access.
PT_GUARDED_BY is similar, but for Pointers and smart Pointers. There are no restrictions on the data members themselves, but the data to which the pointer points is protected by a given capability.
Mutex mu;
int *p1 GUARDED_BY(mu);
int *p2 PT_GUARDED_BY(mu);
unique_ptr<int> p3 PT_GUARDED_BY(mu);
void test(a) {
p1 = 0; // Warning!
*p2 = 42; // Warning!
p2 = new int; // OK.
*p3 = 42; // Warning!
p3.reset(new int); // OK.
}
Copy the code
The REQUIRES (…). , REQUIRES_SHARED (…).
Previously: EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED.
REQUIRES is a compiler property of a function or method that states that the calling thread must have exclusive access to a given capability. Multiple capabilities can be specified. Capabilities must be maintained at function entry as well as at function exit.
REQUIRES_SHARED is similar, but requires only shared access.
Mutex mu1, mu2;
int a GUARDED_BY(mu1);
int b GUARDED_BY(mu2);
void foo(a) REQUIRES(mu1, mu2) {
a = 0;
b = 0;
}
void test(a) {
mu1.Lock();
foo(); // Warning! Requires mu2.
mu1.Unlock();
}
Copy the code
ACQUIRE (…). , ACQUIRE_SHARED (…). To RELEASE (…). , RELEASE_SHARED (…).
Previously: EXCLUSIVE_LOCK_FUNCTION, SHARED_LOCK_FUNCTION, UNLOCK_FUNCTION.
ACQUIRE is a property of a function or method that declares that the function acquired the capability, but does not release it. The caller must not retain the specified capability on entry and will retain it on exit. ACQUIRE_SHARED is similar.
The RELEASE and RELEASE_SHARED declaration functions RELEASE a given capability. The caller must decide how to preserve this capability and not retain it on exit. It does not matter whether the specified capabilities are shared or mutually exclusive.
Mutex mu;
MyClass myObject GUARDED_BY(mu);
void lockAndInit(a) ACQUIRE(mu) {
mu.Lock();
myObject.init();
}
void cleanupAndUnlock(a) RELEASE(mu) {
myObject.cleanup();
} // Warning! Need to unlock mu.
void test(a) {
lockAndInit();
myObject.doSomething();
cleanupAndUnlock();
myObject.doSomething(); // Warning, mu is not locked.
}
Copy the code
If no argument is passed to ACQUIRE or RELEASE, the default argument is this and the parser will not check the function body. This pattern is specific to classes that hide the locking details in a virtual interface. Such as:
template <class T>
class CAPABILITY("mutex") Container {
private:
Mutex mu;
T* data;
public:
// Hide mu from public interface.
void Lock(a) ACQUIRE(a) { mu.Lock(); }
void Unlock(a) RELEASE(a) { mu.Unlock(); }
T& getElem(int i) { returndata[i]; }};void test(a) {
Container<int> c;
c.Lock();
int i = c.getElem(0);
c.Unlock();
}
Copy the code
EXCLUDES (…).
Previously: LOCKS_EXCLUDED.
EXCLUDES is a property of a function or method that states that callers must not hold specified capabilities. Annotations are used to prevent deadlocks. Many implementations of mutex are not reentrant, so if the function acquires the mutex again, it will result in a deadlock.
Mutex mu;
int a GUARDED_BY(mu);
void clear(a) EXCLUDES(mu) {
mu.Lock();
a = 0;
mu.Unlock();
}
void reset(a) {
mu.Lock();
clear(); // Warning! Caller cannot hold 'mu'.
mu.Unlock();
}
Copy the code
Unlike REQUIRES, EXCLUDES is optional. Without this attribute, the profiler does not generate a warning, which can produce false positives in some scenarios. This will be discussed in the following section on false positives.
NO_THREAD_SAFETY_ANALYSIS
NO_THREAD_SAFETY_ANALYSIS is a property of a function or method that turns off thread-safety checks for that method. It turns off checks for functions that are either intentionally non-thread-safe or explicitly thread-safe that are too complex for the parser to understand. Explicit thread-safety is described in detail in the known limits below.
class Counter {
Mutex mu;
int a GUARDED_BY(mu);
void unsafeIncrement(a) NO_THREAD_SAFETY_ANALYSIS { a++; }};Copy the code
Unlike the other attributes, NO_THREAD_SAFETY_ANALYSIS is not part of the function interface and should be placed in the function definition (in.cc or.cpp files) rather than where the function is declared (in the header file).
RETURN_CAPABILITY(c)
Previously: LOCK_RETURNED.
RETURN_CAPABILITY is a property of a function or method that declares that the function returns a reference to the specified capability. It is used to annotate getter methods that return mutex objects.
class MyClass {
private:
Mutex mu;
int a GUARDED_BY(mu);
public:
Mutex* getMu(a) RETURN_CAPABILITY(mu) { return μ }
// analysis knows that getMu() == mu
void clear(a) REQUIRES(getMu()) { a = 0; }};Copy the code
ACQUIRED_BEFORE (…). , ACQUIRED_AFTER (…).
ACQUIRED_BEFORE and ACQUIRED_AFTER are properties of member declarations, especially for mutex or other capability declarations. These declarations maintain a specific order: the mutex must be held first to avoid deadlocks.
Mutex m1;
Mutex m2 ACQUIRED_AFTER(m1);
// Alternative declaration
// Mutex m2;
// Mutex m1 ACQUIRED_BEFORE(m2);
void foo(a) {
m2.Lock();
m1.Lock(); // Warning! m2 must be acquired after m1.
m1.Unlock();
m2.Unlock();
}
Copy the code
CAPABILITY()
Before: LOCKABLE.
A CAPABILITY is an attribute for a class that declares that an object of the class can be used as a CAPABILITY. The string argument specifies what this capability means in an error message, such as a mutex. See the Container sample given above, or the mutex class in mutex.h.
SCOPED_CAPABILITY
Previously: SCOPED_LOCKABLE.
SCOPED_CAPABILITY is an attribute of a class that implements RAII locking mechanisms: it holds capabilities in the constructor and releases capabilities in the destructor. Such classes require special handling because the constructor and destructor access the capability with different names. See the MutexLocker class in mutex.h below.
TRY_ACQUIRE (,…). , TRY_ACQUIRE_SHARED (,…).
Previously: EXCLUSIVE_TRYLOCK_FUNCTION, SHARED_TRYLOCK_FUNCTION.
These are properties of functions or methods that attempt to obtain the specified capability and return a Boolean value indicating success or failure. The first argument must be true or false to specify which return value represents success. The rest of the arguments are understood in the same way as ACQUIRE. See mutex.h below for an example.
ASSERT_CAPABILITY (…). And ASSERT_SHARED_CAPABILITY (…).
Previously: ASSERT_EXCLUSIVE_LOCK, ASSERT_SHARED_LOCK.
These are properties of functions or methods that perform a Runtime test to see if the calling thread already holds the specified capability. When this capability is not held, the function is expected to fail (with no return value). See mutex.h below for an example.
GUARDED_VAR and PT_GUARDED_VAR
This property has been deprecated.
Warning label
- -Wthread-safety: Umbrella flagEnable the following three warnings:
- – wthread-safety-attributes: checks the attribute syntax.
- – wthread-safety-analysis: indicates core analysis.
- – wthread-safety-precise: requires precise matching of mutex expressions. This warning can be disabled in code with a large number of aliases
- – wthread-safety-reference: checks when a guardian member is passed by reference.
Negative Capabilities is an experimental feature that you can enable using the following flag:
- -Wthread-safety-negative: Negative capabilities. This function is disabled by default.
When new features and checks are added to the profiler, they often bring new warnings. These warnings are initially issued as beta warnings and continue for a period of time before being incorporated into standard analysis.
- – wthread-safety-beta: new feature, off by default.
Negative Capabilities
Thread-safety analysis is designed to prevent race conditions and deadlocks. The GUARDED_BY and REQUIRES features prevent race conditions by ensuring that the capability is held before reading or writing the daemon data, while the EXCLUDES property prevents deadlocks by ensuring mutex locks are not held.
However, EXCLUDES is an optional property and does not provide the same security guarantees as REQUIRES. In particular:
- A function which acquires a capability does not have to exclude it.
- A function which calls a function that excludes a capability does not have transitively exclude that capability.
This will lead to such a result, and EXCLUDES is easy to produce false negatives:
class Foo {
Mutex mu;
void foo(a) {
mu.Lock();
bar(); // No warning.
baz(); // No warning.
mu.Unlock();
}
void bar(a) { // No warning. (Should have EXCLUDES(mu)).
mu.Lock();
// ...
mu.Unlock();
}
void baz(a) {
bif(); // No warning. (Should have EXCLUDES(mu)).
}
void bif(a) EXCLUDES(mu);
};
Copy the code
Negative requirements are an alternative EXCLUDES that provide a stronger safety guarantee. A negative requirement uses the REQUIRES attribute, in conjunction with the ! operator, to indicate that a capability should not be held.
For example, use REQUIRES(! Mu) rather than EXCLUDES(mu) will produce the corresponding warning:
class FooNeg {
Mutex mu;
void foo(a) REQUIRES(! mu) { // foo() now requires ! mu.
mu.Lock();
bar();
baz();
mu.Unlock();
}
void bar(a) {
mu.Lock(); // WARNING! Missing REQUIRES(!mu).
// ...
mu.Unlock();
}
void baz(a) {
bif(); // WARNING! Missing REQUIRES(!mu).
}
void bif(a) REQUIRES(! mu);
};
Copy the code
Negative requirements are an experimental feature which is off by default, because it will produce many warnings in existing code. It can be enabled by passing -Wthread-safety-negative.
FAQ
Q: Do you need to put properties in a header file, or do you need to implement.cc/.cpp/.cxx? (A) Attributes are part of the formal interface to A function and need to always be in the header file so that they are visible to any code that contains the header file. Attributes in the.cpp file are not externally visible and can cause false positives.
Q: “Mutex locks are not applied in every scenario.” What does that mean? See removing conditional locks below.
Known limitations
Lexical scope
Thread-safety analysis includes normal C++ expressions that follow normal C++ scoping rules. In particular, this means that mutex and other capabilities must be declared in advance before they are used. Use-before-declaration is possible in a separate class because the attributes are analyzed at the same time as the method body. (C++ doesn’t parse the method body until the bottom of the class file.) However, use before declaration is not allowed between classes, as follows:
class Foo;
class Bar {
void bar(Foo* f) REQUIRES(f->mu); // Error: mu undeclared.
};
class Foo {
Mutex mu;
};
Copy the code
Private mutex
Good software engineering practice dictates that a mutex should be a private member because the locking mechanism used by a thread-safe class is part of the internal implementation of the class. However, private mutex can sometimes leak to a class’s public interface. The thread-safe property follows common C++ access restrictions, so if mu is a private member of C, name accessing C. mu in a property will fail.
One solution is to use the RETURN_CAPABILITY attribute to provide an external name for the private mutex without actually exposing the internal mutex. Such as:
class MyClass {
private:
Mutex mu;
public:
// For thread safety analysis only. Does not actually return mu.
Mutex* getMu(a) RETURN_CAPABILITY(mu) { return 0; }
void doSomething(a) REQUIRES(mu);
};
void doSomethingTwice(MyClass& c) REQUIRES(c.getMu()) {
// The analysis thinks that c.getMu() == c.mu
c.doSomething();
c.doSomething();
}
Copy the code
In the above example, doSomethingTwice() is an external call that requires c. Mu to be locked, whereas MU is private and cannot be accessed directly from outside. This approach is not recommended because it violates the specification of encapsulation, but it is sometimes necessary, especially if you need to add annotations to existing code. Part of the solution is to define a function getMu() as a false getter method, just for the benefit of thread-safety analysis.
Removing conditional locks
The profiler must be able to determine whether a lock is being held at any time the program is running. As a result, a piece of code that might hold a lock can produce some false warnings (such as false positives). Such as:
English original text: The analysis must be able to determine whether a lock is held, or not held, at every program point. Thus, sections of code where a lock might be held will generate spurious warnings (false positives).
void foo(a) {
bool b = needsToLock();
if(b) mu.Lock(); .// Warning! Mutex 'mu' is not held on every path through here.
if (b) mu.Unlock();
}
Copy the code
Remove checks in constructors and destructors
The thread safety analyzer does not currently do any checks inside constructors and destructors. In other words, any constructor or destructor is treated as if the NO_THREAD_SAFETY_ANALYSIS tag has been used. The reason for this is that during initialization, only one thread will access the object, the thread responsible for initializing the object. It is safe (a common code practice) to initialize the member that needs to be locked without acquiring the lock. The same is true for destructors.
Ideally, within the initialization or destruction of an object, the profiler allows initialization that requires locking members, while the usual access restrictions for other scenarios remain in place. However, this is really difficult to implement in real code practice, because in complex pointer related data structures, it is difficult to determine which data is owned by a closed object.
Remove the inline
Thread safety analysis is strictly intra-procedural, just like normal type checking. It relies only on the declared attributes of a function, and will not attempt to inline any method calls. Therefore, code like the following may not work:
template<class T>
class AutoCleanup {
T* object;
void (T::*mp)();
public:
AutoCleanup(T* obj, void(T::*imp)()) : object(obj), mp(imp) { } ~AutoCleanup() { (object->*mp)(); }}; Mutex mu;void foo(a) {
mu.Lock();
AutoCleanup<Mutex>(&mu, &Mutex::Unlock);
// ...
} // Warning, mu is not unlocked.
Copy the code
In this scenario, the destructor of Autocleanup calls mu.unlock (), so the warning is incorrect. However, the thread-safe analyzer cannot see this unlocking operation because it does not attempt to expand the destructor inline. Also, there is no way to annotate the destructor, because the destructor calls a static unknown function. This mode is simply not supported.
Remove alias analysis
The analysis does not currently track pointer aliases. However, if both Pointers point to the same mutex, this can result in false positives.
class MutexUnlocker {
Mutex* mu;
public: MutexUnlocker(Mutex* m) RELEASE(m) : mu(m) { mu->Unlock(); } ~MutexUnlocker() ACQUIRE(mu) { mu->Lock(); }}; Mutex mutex;void test(a) REQUIRES(mutex) {{MutexUnlocker munl(&mutex); // unlocks mutex
doSomeIO();
} // Warning: locks munl.mu
}
Copy the code
The MutexUnlocker class is the reverse capability implementation of the MutexLocker class, declared in mutex.h. However, it does not work because the profiler does not know that munl.mu is equal to mutex. The SCOPED_CAPABILITY attribute handles MutexLocker aliases, but only for specific patterns.
ACQUIRED_BEFORE (…). And ACQUIRED_AFTER (…). Not yet implemented
This will be fixed in a future update.
mutex.h
Thread-safety analysis can be used in any threading library, but requires the thread API to be wrapped in properly annotated classes and methods. The following code provides mutex.h as an instance, and these methods should populate their implementation in order to invoke the appropriate underlying implementation.
#ifndef THREAD_SAFETY_ANALYSIS_MUTEX_H
#define THREAD_SAFETY_ANALYSIS_MUTEX_H
// Enable thread safety attributes only with clang.
// The attributes can be safely erased when compiling with other compilers.
#ifdefined(__clang__) && (! defined(SWIG))
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif
#define CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
#define SCOPED_CAPABILITY \
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
#define GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
#define PT_GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
#define ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
#define ACQUIRED_AFTER(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
#define REQUIRES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
#define REQUIRES_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
#define ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
#define ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
#define RELEASE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
#define RELEASE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
#define TRY_ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
#define TRY_ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
#define EXCLUDES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
#define ASSERT_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
#define ASSERT_SHARED_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
#define RETURN_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
// Defines an annotated interface for mutexes.
// These methods can be implemented to use any internal mutex implementation.
class CAPABILITY("mutex") Mutex {
public:
// Acquire/lock this mutex exclusively. Only one thread can have exclusive
// access at any one time. Write operations to guarded data require an
// exclusive lock.
void Lock(a) ACQUIRE(a);
// Acquire/lock this mutex for read operations, which require only a shared
// lock. This assumes a multiple-reader, single writer semantics. Multiple
// threads may acquire the mutex simultaneously as readers, but a writer
// must wait for all of them to release the mutex before it can acquire it
// exclusively.
void ReaderLock(a) ACQUIRE_SHARED(a);
// Release/unlock an exclusive mutex.
void Unlock(a) RELEASE(a);
// Release/unlock a shared mutex.
void ReaderUnlock(a) RELEASE_SHARED(a);
// Try to acquire the mutex. Returns true on success, and false on failure.
bool TryLock(a) TRY_ACQUIRE(true);
// Try to acquire the mutex for read operations.
bool ReaderTryLock(a) TRY_ACQUIRE_SHARED(true);
// Assert that this mutex is currently held by the calling thread.
void AssertHeld(a) ASSERT_CAPABILITY(this);
// Assert that is mutex is currently held for read operations.
void AssertReaderHeld(a) ASSERT_SHARED_CAPABILITY(this);
// For negative capabilities.
const Mutex& operator! (a)const { return *this; }};// MutexLocker is an RAII class that acquires a mutex in its constructor, and
// releases it in its destructor.
class SCOPED_CAPABILITY MutexLocker {
private:
Mutex* mut;
public: MutexLocker(Mutex *mu) ACQUIRE(mu) : mut(mu) { mu->Lock(); } ~MutexLocker() RELEASE() { mut->Unlock(); }};#ifdef USE_LOCK_STYLE_THREAD_SAFETY_ATTRIBUTES
// The original version of thread safety analysis the following attribute
// definitions. These use a lock-based terminology. They are still in use
// by existing thread safety code, and will continue to be supported.
// Deprecated.
#define PT_GUARDED_VAR \
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_var)
// Deprecated.
#define GUARDED_VAR \
THREAD_ANNOTATION_ATTRIBUTE__(guarded_var)
// Replaced by REQUIRES
#define EXCLUSIVE_LOCKS_REQUIRED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
// Replaced by REQUIRES_SHARED
#define SHARED_LOCKS_REQUIRED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
// Replaced by CAPABILITY
#define LOCKABLE \
THREAD_ANNOTATION_ATTRIBUTE__(lockable)
// Replaced by SCOPED_CAPABILITY
#define SCOPED_LOCKABLE \
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
// Replaced by ACQUIRE
#define EXCLUSIVE_LOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
// Replaced by ACQUIRE_SHARED
#define SHARED_LOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
// Replaced by RELEASE and RELEASE_SHARED
#define UNLOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
// Replaced by TRY_ACQUIRE
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
// Replaced by TRY_ACQUIRE_SHARED
#define SHARED_TRYLOCK_FUNCTION(...) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
// Replaced by ASSERT_CAPABILITY
#define ASSERT_EXCLUSIVE_LOCK(...) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
// Replaced by ASSERT_SHARED_CAPABILITY
#define ASSERT_SHARED_LOCK(...) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__))
// Replaced by EXCLUDE_CAPABILITY.
#define LOCKS_EXCLUDED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
// Replaced by RETURN_CAPABILITY
#define LOCK_RETURNED(x) \
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
#endif // USE_LOCK_STYLE_THREAD_SAFETY_ATTRIBUTES
#endif // THREAD_SAFETY_ANALYSIS_MUTEX_H
Copy the code