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

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

  • 相关阅读:
    double 和 int 同时存在时的运算
    快速排序
    案例:商品放大镜效果
    淘宝flexible.js源码分析
    案例:模态框拖拽
    Web APIs——BOM
    案例:获取URL参数数据
    案例:5秒之后自动跳转页面
    JS中this指针的指向
    按钮:点击发送短信按钮60秒内不能再次点击的功能
  • 原文地址:https://www.cnblogs.com/cherishui/p/10962161.html
Copyright © 2020-2023  润新知