本文内容来自 Thread Safety Analysis,如需完整学习,请参考相关连接。html
Clang线程安全分析工具是C++语言的一种扩展,用于警告代码中潜在的竞争条件。它在编译期间进行静态分析,无运行期性能损耗。即便该工具仍处在开发阶段,但已足够成熟,适合部署在生产环境上。编程
它的工做原理相似于一个针对多线程编程的类型系统。例如,变量 foo
可被多线程访问,当分析工具检测到该变量在读写时没有被对应的锁所保护时,会提示一个警告。安全
clang的线程安全分析工具使用capabilities
来保护资源。这里的资源指的是成员数据,或者是提供访问底层资源的函数或方法。它能确保调用线程只有先拥有capability
,才能访问对应资源。多线程
假如 mu
表明一个 mutex
,当线程执行 mu.Lock()
后,表明它得到了访问 mu
所保护资源的 capability
,当它调用 mu.unLock()
后,表明它释放了该 capability
。该线程拥有的capability
是不可拷贝的,也不能销毁它,它只能释放该capability
,以便其余线程得到该capability
。函数
线程安全分析工具使用属性注解来声明依赖,这些属性注解是附加在类、方法、数据成员之上的。官方推荐使用宏(mutex.h)来使属性注解可读性更好、可理解。工具
运行方法很简单,执行性能
clang -c -Wthread-safety example.cpp
GUARDED_BY: 该属性声明,线程在对该变量进行读写以前,必定要得到对应的锁,以确保对该变量的操做是线程安全的。学习
PT_GUARDED_BY: 和GUARDED_BY相似,它用于指针和智能指针上,对指针自身没有约束,但对它所指向的数据施加属性保护。ui
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. }
REQUIRES(mu) 指示调用线程在调用该函数前,必须先得到mu锁。它假设调用者在调用该函数前,已经拥有mu锁,内部对共享变量的修改无需额外加锁。this
REQUIRES_SHARED(), 与 REQUIRES 相似,但仅要求共享读访问权限。
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(); }
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 ,分析工具不会检查函数内部实现。经常使用在隐藏抽象接口的内部锁的实现细节。
capabilities
, 该注解用来预防死锁,对于不可重入的锁,当同一个函数重入时,会得到2次锁,形成死锁。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(); }
CAPABILITY(
capaility
,string参数用来代表该
capaility
的种类,在警告时会输出。
SCOPED_CAPABILITY:指明该类用于RAII风格的资源管理。