参考:Clang 15.0.0git documentation THREAD SAFETY ANALYSIS
1. 简介
Clang线程安全分析是一种C++扩展,代码中存在潜在的竞争时会发出警告。线程安全分析是完全静态的(即编译时);没有运行时开销。该线程安全分析仍在积极开发中,但已经足够成熟,可以在工业环境中部署。它由谷歌与CERT/SEI合作开发,广泛用于谷歌的内部代码库。
线程安全性分析的工作原理非常类似于多线程程序的类型系统。除了声明数据类型(例如int、float等),程序员还可以(可选)声明在多线程环境中如何控制对该数据的访问。例如,如果foo由mutex mu保护,那么每当一段代码在没有首先锁定mu的情况下读取或写入foo时,分析就会发出警告。类似地,如果存在只应由GUI线程调用的特定例程,那么如果其他线程调用这些例程,分析将发出警告。
2. 开始
#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();
}
};
这个例子展示了线程安全分析背后的基本概念。GUARDED_BY属性声明线程必须先锁定mu,然后才能读写balance,从而确保递增和递减操作是原子的。类似地,REQUIRES声明调用线程必须在调用withdrawImpl之前锁定mu。由于假定调用方已锁定mu,因此可以安全地修改方法中的balance 。
depositImpl()方法没有REQUIRES,因此分析会发出警告。线程安全分析不是过程间的,因此必须明确声明调用方需求。transferFrom()中还有一个警告,因为尽管该方法锁定了this->mu,但它不会锁定b.mu。分析表明,这是两个不同对象中的两个独立mutex。
最后,withdraw()方法中还有一个警告,因为它无法解锁mu。每个锁都必须有相应的解锁,分析将检测双重锁定和双重解锁。函数可以在不释放锁的情况下获取锁(反之亦然),但必须对其进行注释(使用ACQUIRE/RELEASE)。
3. 运行分析
要运行分析,只需使用-Wthread-safety flag,例如
clang -c -Wthread-safety example.cpp
请注意,本例假设存在一个经过适当注释的mutex声明了哪些方法执行锁定、解锁等操作。
4. 基本概念:Capabilities
线程安全分析提供了一种利用功能(Capabilities)保护资源的方法。资源要么是数据成员,要么是提供对某个底层资源的访问的函数/方法。分析确保调用线程无法访问资源(即调用函数或读/写数据),除非它有能力(capability)这样做。
能力(Capabilities)与命名C++对象相关联,这些对象声明特定方法来获取和释放capability。对象的名称用于标识capability。最常见的例子是mutex。例如,如果mu是mutex,则调用mu.Lock()使调用线程获得访问受mu保护的数据的capability。同样,调用mu.Unlock()释放了这种capability。
线程可以独占或共享某个功能(capability)。独占功能一次只能由一个线程持有,而共享功能可以由多个线程同时持有。这种机制实施了多读单写模式。对受保护数据的写入操作需要独占访问,而读取操作只需要共享访问。
5. 参考指南
线程安全性分析使用属性(attributes)来声明线程约束。属性必须附加到命名声明,例如类、方法和数据成员。强烈建议用户为各种属性定义宏;示例定义可以参考下面mutex.h。
这些属性仅控制线程安全分析所做的假设及其发出的警告。它们不会影响运行时生成的代码或行为。
6. GUARDED_BY和PT_GUARDED_BY
GUARDED_BY是数据成员的一个属性(attribute ),它声明数据成员受给定功能(capability)的保护。对数据的读操作需要共享访问,而写操作需要独占访问。
PT_GUARDED_BY与之类似,但用于指针和智能指针。数据成员本身没有约束,但它指向的数据受给定功能(capability)的保护。
Mutex mu;
int *p1 GUARDED_BY(mu);
int *p2 PT_GUARDED_BY(mu);
unique_ptr<int> p3 PT_GUARDED_BY(mu);
void test() {
p1 = 0; // Warning!
*p2 = 42; // Warning!
p2 = new int; // OK.
*p3 = 42; // Warning!
p3.reset(new int); // OK.
}
7. REQUIRES(…), REQUIRES_SHARED(…)
Previously: EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED
REQUIRES是函数或方法的一个属性(attribute),它声明调用线程必须以独占方式访问给定的功能(capabilities)。
可以指定多个功能。这些功能必须在进入功能时保持,在退出功能时仍然保持。
REQUIRES_SHARED类似,但只需要共享访问。
Mutex mu1, mu2;
int a GUARDED_BY(mu1);
int b GUARDED_BY(mu2);
void foo() REQUIRES(mu1, mu2) {
a = 0;
b = 0;
}
void test() {
mu1.Lock();
foo(); // Warning! Requires mu2.
mu1.Unlock();
}
8. ACQUIRE(…), ACQUIRE_SHARED(…), RELEASE(…), RELEASE_SHARED(…), RELEASE_GENERIC(…)
Previously: EXCLUSIVE_LOCK_FUNCTION, SHARED_LOCK_FUNCTION, UNLOCK_FUNCTION
ACQUIRE和ACQUIRE_SHARED是函数或方法上的属性(attributes ),声明函数获取了功能(capability),但没有释放它。
给定的能力不能在进入时保持,而将在退出时保持(独占用ACQUIRE,共享用ACQUIRE_SHARED)。
RELEASE、RELEASE_SHARED和RELEASE_GENERIC声明函数释放给定的功能(capability)。该能力必须在进入时保持(独占用RELEASE、共享用RELEASE_SHARED、独占或共享用RELEASE_GENERIC),并且在退出时不再保持。
Mutex mu;
MyClass myObject GUARDED_BY(mu);
void lockAndInit() ACQUIRE(mu) {
mu.Lock();
myObject.init();
}
void cleanupAndUnlock() RELEASE(mu) {
myObject.cleanup();
} // Warning! Need to unlock mu.
void test() {
lockAndInit();
myObject.doSomething();
cleanupAndUnlock();
myObject.doSomething(); // Warning, mu is not locked.
}
如果没有给ACQUIRE或RELEASE传递参数,则假定该参数为this,并且分析不会检查函数体。
此模式旨在供将锁定细节隐藏在抽象接口后面的类使用。例如:
template <class T>
class CAPABILITY("mutex") Container {
private:
Mutex mu;
T* data;
public:
// Hide mu from public interface.
void Lock() ACQUIRE() { mu.Lock(); }
void Unlock() RELEASE() { mu.Unlock(); }
T& getElem(int i) { return data[i]; }
};
void test() {
Container<int> c;
c.Lock();
int i = c.getElem(0);
c.Unlock();
}
9. EXCLUDES(…)
Previously: LOCKS_EXCLUDED
EXCLUDES是函数或方法的一个属性(attribute),它声明调用方不能持有给定的功能(capabilities)。此注释用于防止死锁。许多mutex实现都不是可重入的,因此如果函数第二次获取mutex,可能会发生死锁。
Mutex mu;
int a GUARDED_BY(mu);
void clear() EXCLUDES(mu) {
mu.Lock();
a = 0;
mu.Unlock();
}
void reset() {
mu.Lock();
clear(); // Warning! Caller cannot hold 'mu'.
mu.Unlock();
}
与REQUIRES不同,EXCLUDES是可选的。如果属性(attribute)缺失,分析不会发出警告,这在某些情况下可能会导致误判。这个问题将在Negative Capabilities中进一步讨论。
10. NO_THREAD_SAFETY_ANALYSIS
NO_THREAD_SAFETY_ANALYSIS是函数或方法的一个属性(attribute ),用于关闭该方法的线程安全检查。它为(1)故意线程不安全的函数或(2)线程安全的函数提供了一个逃生舱口,但这些函数太复杂,分析无法理解。(2)的原因将在下面的已知限制中描述。
class Counter {
Mutex mu;
int a GUARDED_BY(mu);
void unsafeIncrement() NO_THREAD_SAFETY_ANALYSIS { a++; }
};
与其他属性不同,NO_THREAD_SAFETY_分析不是函数接口的一部分,因此应该放在函数定义(在.cc或.cpp文件中)而不是函数声明(在头文件中)上。
11. RETURN_CAPABILITY
Previously: LOCK_RETURNED
RETURN_CAPABILITY是函数或方法的一个属性(attribute),它声明函数返回对给定功能的引用。它用于注释返回mutex的getter方法。
class MyClass {
private:
Mutex mu;
int a GUARDED_BY(mu);
public:
Mutex* getMu() RETURN_CAPABILITY(mu) { return μ }
// analysis knows that getMu() == mu
void clear() REQUIRES(getMu()) { a = 0; }
};
12. ACQUIRED_BEFORE(…), ACQUIRED_AFTER(…)
ACQUIRED_BEFORE和ACQUIRED_AFTER是成员声明的属性(attributes),特别是mutexes或其他功能(capabilities)的声明。这些声明强制必须以特定顺序获取mutexes,以防止死锁。
Mutex m1;
Mutex m2 ACQUIRED_AFTER(m1);
// Alternative declaration
// Mutex m2;
// Mutex m1 ACQUIRED_BEFORE(m2);
void foo() {
m2.Lock();
m1.Lock(); // Warning! m2 must be acquired after m1.
m1.Unlock();
m2.Unlock();
}
13. CAPABILITY()
Previously: LOCKABLE
CAPABILITY是类的一个属性(attribute),它指定类的对象可以被当做一个功能(capability)。字符串参数指定错误消息中的功能类型,例如mutex。请参见上面给出的示例,或mutex.h中的Mutex类。
14. SCOPED_CAPABILITY
Previously: SCOPED_LOCKABLE
SCOPED_CAPABILITY是实现RAII风格锁定的类上的一个属性(attribute),在该类中,一个功能(capability)在构造函数中获得,并在析构函数中释放。此类类需要特殊处理,因为构造函数和析构函数通过不同的名称引用功能;请参见下面mutex.h中的MutexLocker类。
作用域能力(capabilities)被视为在构造时隐式获取并在销毁时释放的能力。 它们与构造函数的线程安全属性中命名的一组(常规)功能相关联。
其他成员函数上的Acquire-type属性被视为应用于该组关联功能,而RELEASE意味着函数以任何模式释放所有关联功能。
15. TRY_ACQUIRE(, …), TRY_ACQUIRE_SHARED(, …)
Previously: EXCLUSIVE_TRYLOCK_FUNCTION, SHARED_TRYLOCK_FUNCTION
这些是函数或方法的属性,该函数或方法尝试获取给定的功能,并返回一个布尔值,指示成功或失败。第一个参数必须为true或false,以指定哪个返回值表示成功,其余参数的解释方式与ACQUIRE相同。参见下面的mutex.h。
因为分析不支持条件锁定,所以在try-acquire函数返回值的第一个分支之后,功能被视为已获取。
Mutex mu;
int a GUARDED_BY(mu);
void foo() {
bool success = mu.TryLock();
a = 0; // Warning, mu is not locked.
if (success) {
a = 0; // Ok.
mu.Unlock();
} else {
a = 0; // Warning, mu is not locked.
}
}
16. ASSERT_CAPABILITY(…) and ASSERT_SHARED_CAPABILITY(…)
Previously: ASSERT_EXCLUSIVE_LOCK, ASSERT_SHARED_LOCK
这些是一个函数或方法的属性,该函数或方法断言调用线程已经拥有给定的功能,例如通过执行运行时测试并在未拥有能力(capability)时终止。此注释的存在会让线程安全分析(Thread Safety Analysis)假设在调用带注释的函数后保持该功能。参见下面的 mutex.h。
17. GUARDED_VAR and PT_GUARDED_VAR
不推荐使用这些属性。
18. Warning flags
-Wthread-safety
-Wthread-safety-attributes: 语义检查线程安全属性
-Wthread-safety-analysis:核心分析
-Wthread-safety-precise:要求mutex表达式精确匹配
对于有很多别名的代码,可以禁用此警告。
-Wthread-safety-reference:检查通过引用传递的受保护成员时
Negative Capabilities 是一项实验性功能,可通过以下方式启用:
-Wthread-safety-negative:-Wthread-safety-negative:负面能力,默认关闭
当新功能和检查添加到分析中时,它们通常会引入额外的警告。这些警告最初作为测试版警告发布一段时间,之后将迁移到标准分析中。
-Wthread-safety-beta:-Wthread安全测试版:新功能,默认情况下关闭
19. Negative Capabilities
线程安全分析旨在防止竞争条件和死锁。 GUARDED_BY 和 REQUIRES 属性通过确保在读取或写入受保护的数据之前保持能力(capability)来防止竞争条件,而 EXCLUDES 属性通过确保不保持mutex来防止死锁。
但是,EXCLUDES 是一个可选属性,不提供与 REQUIRES 相同的安全保证。 特别是:
获得某种能力的函数不必排除它。
调用排除某个功能的函数的函数没有传递性地排除该功能。
结果,EXCLUDES 很容易产生误报:
class Foo {
Mutex mu;
void foo() {
mu.Lock();
bar(); // No warning.
baz(); // No warning.
mu.Unlock();
}
void bar() { // No warning. (Should have EXCLUDES(mu)).
mu.Lock();
// ...
mu.Unlock();
}
void baz() {
bif(); // No warning. (Should have EXCLUDES(mu)).
}
void bif() EXCLUDES(mu);
};
负面要求是一种提供更强安全保障的替代方案。负面需求使用REQUIRES属性,与!操作符结合,表示不应保持某一能力。
例如,使用REQUIRES(!mu)而不是EXCLUDES(mu)将产生适当的警告:
class FooNeg {
Mutex mu;
void foo() REQUIRES(!mu) { // foo() now requires !mu.
mu.Lock();
bar();
baz();
mu.Unlock();
}
void bar() {
mu.Lock(); // WARNING! Missing REQUIRES(!mu).
// ...
mu.Unlock();
}
void baz() {
bif(); // WARNING! Missing REQUIRES(!mu).
}
void bif() REQUIRES(!mu);
};
负面需求是一个实验性的特性,默认情况下是关闭的,因为它会在现有代码中产生许多警告。它可以通过传递-Wthread safety-negative来启用。
20. Frequently Asked Questions
Q.我应该将属性放在头文件中,还是放在 .cc/.cpp/.cxx 文件中?
A.属性是函数正式接口的一部分,应该始终放在头文件中,引入头文件的任何地方都可以看到它们。.cpp文件中的属性在直接翻译单元之外不可见,这会导致误报。
Q.“互斥锁不是锁在通过这里的每条路径上吗?” 那是什么意思?
A.参见下文No conditionally held locks.
21. Known Limitations
Lexical scope
线程安全属性包含普通的 C++ 表达式,因此遵循普通的 C++ 范围规则。 特别是,这意味着必须先声明互斥锁和其他功能,然后才能在属性中使用它们。 Use-before-declaration 在单个类中是可以的,因为属性与方法体同时被解析。 (C++ 将方法体的解析延迟到类的末尾。)但是,类之间不允许使用前声明,如下所示。
class Foo;
class Bar {
void bar(Foo* f) REQUIRES(f->mu); // Error: mu undeclared.
};
class Foo {
Mutex mu;
};
22. Private Mutexes
良好的软件工程实践要求互斥锁应该是私有成员,因为线程安全类使用的锁定机制是其内部实现的一部分。 但是,私有互斥锁有时会泄漏到类的公共接口中。 线程安全属性遵循正常的 C++ 访问限制,所以如果 mu 是 c 的私有成员,那么在属性中写入 c.mu 是错误的。
一种解决方法是使用 RETURN_CAPABILITY 属性为私有互斥体提供公共名称,而不是暴露底层互斥体。 例如:
class MyClass {
private:
Mutex mu;
public:
// For thread safety analysis only. Does not need to be defined.
Mutex* getMu() RETURN_CAPABILITY(mu);
void doSomething() REQUIRES(mu);
};
void doSomethingTwice(MyClass& c) REQUIRES(c.getMu()) {
// The analysis thinks that c.getMu() == c.mu
c.doSomething();
c.doSomething();
}
在上面的例子中,doSomethingTwice() 是一个外部例程,需要锁定 c.mu,因为 mu 是私有的,所以不能直接声明。 不鼓励这种模式,因为它违反了封装,但有时它是必要的,尤其是在向现有代码库添加注释时。 解决方法是将 getMu() 定义为伪 getter 方法,该方法仅用于线程安全分析。
23. No conditionally held locks
分析必须能够确定在每个程序点是否持有锁。因此,可能持有锁的代码部分将生成虚假警告(误报)。例如:
void foo() {
bool b = needsToLock();
if (b) mu.Lock();
... // Warning! Mutex 'mu' is not held on every path through here.
if (b) mu.Unlock();
}
24. No checking inside constructors and destructors
该分析目前没有在构造函数或析构函数内部进行任何检查。换句话说,每一个构造函数和析构函数都被视为带有NO_THREAD_SAFETY_ANALYSIS的注释。原因是,在初始化期间,通常只有一个线程可以访问正在初始化的对象,因此在不获取任何锁的情况下初始化受保护的成员是安全的(也是常见的做法)。析构函数也是如此。
理想情况下,分析将允许初始化或销毁对象内的受保护成员,同时仍对其他所有内容实施通常的访问限制。 然而,这在实践中很难实施,因为在复杂的基于指针的数据结构中,很难确定封闭对象拥有哪些数据。
25. No inlining
线程安全性分析是严格的过程内分析,就像普通类型检查一样。它仅依赖于函数的声明属性,而不依赖于函数的任何内联属性。因此,以下代码将不起作用:
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() {
mu.Lock();
AutoCleanup<Mutex>(&mu, &Mutex::Unlock);
// ...
} // Warning, mu is not unlocked.
在这种情况下,Autocleanup 的析构函数调用 mu.Unlock(),因此警告是虚假的。 但是,线程安全分析看不到解锁,因为它不会尝试内联析构函数。 此外,没有办法对析构函数进行注释,因为析构函数正在调用一个静态未知的函数。 根本不支持这种模式。
26. No alias analysis
该分析目前不跟踪指针别名。因此,如果两个指针都指向同一个互斥锁,可能会出现误报。
class MutexUnlocker {
Mutex* mu;
public:
MutexUnlocker(Mutex* m) RELEASE(m) : mu(m) { mu->Unlock(); }
~MutexUnlocker() ACQUIRE(mu) { mu->Lock(); }
};
Mutex mutex;
void test() REQUIRES(mutex) {
{
MutexUnlocker munl(&mutex); // unlocks mutex
doSomeIO();
} // Warning: locks munl.mu
}
在 mutex.h中定义的MutexUnlocker类旨在成为MutexLocker类的对偶。 但是,它不起作用,因为分析不知道munl.mu == mutex。SCOPED_CAPABILITY属性处理 MutexLocker 的别名,但仅针对该特定模式。
27. ACQUIRED_BEFORE(…) and ACQUIRED_AFTER(…) are currently unimplemented.
将在将来的更新中修复。
28. mutex.h
#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.
#if defined(__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 RELEASE_GENERIC(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_generic_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() ACQUIRE();
// 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() ACQUIRE_SHARED();
// Release/unlock an exclusive mutex.
void Unlock() RELEASE();
// Release/unlock a shared mutex.
void ReaderUnlock() RELEASE_SHARED();
// Generic unlock, can unlock exclusive and shared mutexes.
void GenericUnlock() RELEASE_GENERIC();
// Try to acquire the mutex. Returns true on success, and false on failure.
bool TryLock() TRY_ACQUIRE(true);
// Try to acquire the mutex for read operations.
bool ReaderTryLock() TRY_ACQUIRE_SHARED(true);
// Assert that this mutex is currently held by the calling thread.
void AssertHeld() ASSERT_CAPABILITY(this);
// Assert that is mutex is currently held for read operations.
void AssertReaderHeld() ASSERT_SHARED_CAPABILITY(this);
// For negative capabilities.
const Mutex& operator!() const { return *this; }
};
// Tag types for selecting a constructor.
struct adopt_lock_t {} inline constexpr adopt_lock = {};
struct defer_lock_t {} inline constexpr defer_lock = {};
struct shared_lock_t {} inline constexpr shared_lock = {};
// 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;
bool locked;
public:
// Acquire mu, implicitly acquire *this and associate it with mu.
MutexLocker(Mutex *mu) ACQUIRE(mu) : mut(mu), locked(true) {
mu->Lock();
}
// Assume mu is held, implicitly acquire *this and associate it with mu.
MutexLocker(Mutex *mu, adopt_lock_t) REQUIRES(mu) : mut(mu), locked(true) {}
// Acquire mu in shared mode, implicitly acquire *this and associate it with mu.
MutexLocker(Mutex *mu, shared_lock_t) ACQUIRE_SHARED(mu) : mut(mu), locked(true) {
mu->ReaderLock();
}
// Assume mu is held in shared mode, implicitly acquire *this and associate it with mu.
MutexLocker(Mutex *mu, adopt_lock_t, shared_lock_t) REQUIRES_SHARED(mu)
: mut(mu), locked(true) {}
// Assume mu is not held, implicitly acquire *this and associate it with mu.
MutexLocker(Mutex *mu, defer_lock_t) EXCLUDES(mu) : mut(mu), locked(false) {}
// Release *this and all associated mutexes, if they are still held.
// There is no warning if the scope was already unlocked before.
~MutexLocker() RELEASE() {
if (locked)
mut->GenericUnlock();
}
// Acquire all associated mutexes exclusively.
void Lock() ACQUIRE() {
mut->Lock();
locked = true;
}
// Try to acquire all associated mutexes exclusively.
bool TryLock() TRY_ACQUIRE(true) {
return locked = mut->TryLock();
}
// Acquire all associated mutexes in shared mode.
void ReaderLock() ACQUIRE_SHARED() {
mut->ReaderLock();
locked = true;
}
// Try to acquire all associated mutexes in shared mode.
bool ReaderTryLock() TRY_ACQUIRE_SHARED(true) {
return locked = mut->ReaderTryLock();
}
// Release all associated mutexes. Warn on double unlock.
void Unlock() RELEASE() {
mut->Unlock();
locked = false;
}
// Release all associated mutexes. Warn on double unlock.
void ReaderUnlock() RELEASE() {
mut->ReaderUnlock();
locked = false;
}
};
#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