Clang的线程安全分析静态工具

本文内容来自 Thread Safety Analysis,如需完整学习,请参考相关链接。

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类似,它用于指针和智能指针上,对指针自身没有约束,但对它所指向的数据施加属性保护。

    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锁,内部对共享变量的修改无需额外加锁。

  • 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(); }

  • ACQUIRE() 声明要求函数内部获得锁,直到退出该函数也无需释放它。调用者在调用该函数前,不能持有该锁。

  • RELEASE() 声明函数具备释放锁的能力,调用者在调用入口处无需持有该锁,函数退出前会主动释放该锁。

    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 ,分析工具不会检查函数内部实现。常用在隐藏抽象接口的内部锁的实现细节。

  • EXCLUDES(...) 声明调用者不能持有给定 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(); }

  • NO_THREAD_SAFETY_ANALYSIS: 关闭线程安全检查,该属性不属于声明的一部分,使用时要放在 .cpp 文件中。

适用于类的注解

  • CAPABILITY(<string>): 指明该类的实例可被用作 capaility,string参数用来表明该 capaility 的种类,在警告时会输出。

  • SCOPED_CAPABILITY:指明该类用于RAII风格的资源管理。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )