静态检查工具:Clang thread safety annotations 参考连接:Thread Safety Analysis¶

总览

线程安全分析的工作原理非常类似于多线程程序的类型系统。除了声明数据类型(例如intfloat等)之外,程序员还可以(选择性地)声明如何在多线程环境中控制对数据的访问。例如,如果foo是由互斥锁mu保护的,那么当一段代码在没有首先锁定mu的情况下对foo进行读写时,分析就会发出警告。类似地,如果有一些特定的例程应该只由GUI线程调用,那么分析将在其他线程调用这些例程时发出警告。

主要宏一览

GUARDED_BY(mu) 数据成员收到mu保护,读共享,写排他(即写前锁)
REQUIRES(mu) 调用函数或方法前,需要mu,即函数进入前已持有,退出后仍持有
ACQUIRE(mu) 调用函数或方法时,持有mu,即函数进入时才持有,退出后不释放
RELEASE(mu) 调用函数或方法时,释放mu,即函数进入前已持有,退出前释放
EXCLUDES(mu) 调用函数或方法时,不需mu,即函数进入前不能持有,退出后自然不能释放
NO_THREAD_SAFETY_ANALYSIS 调用函数或方法时,关闭对其线程安全检查
RETURN_CAPABILITY(mu) 声明函数返回对给定能力的引用,用于注释返回互斥对象的getter方法。
CAPABILITY(<string>) 指定类的对象(this)可以作为能力使用,配合无参数ACQUIRERELEASE使用
SCOPE_CAPABILITY 实现RAII-style锁的类的一个属性,其功能在构造函数中获得,在析构函数中释放
TRY_ACQUIRE<bool,mu> 试图获得给定的功能,并返回一个指示成功或失败的布尔值。
ASSET_CAPABILITY(mu) 它断言调用线程已经拥有给定的能力
  • 其余关键组合词(如ACQUIRE与ACQUIRE_SHARED)

    • PT pointer
    • SHARED 支持共享访问
    • GENERIC 支持独占和共享访问

      入门实操

      通过代码注解(annotations)告诉编译器哪些成员变量和成员函数受mutex保护,若忘记加锁,编译器给出警告。
      目的:
      他人后续维护代码时,特别容易遗漏线程安全的假设。此工具能将原作者设计意图以代码注解清楚地写出来并让编译器【自动检查】!
      注意:
  • GUARDED_BY 表明哪个成员变量被mutex保护

  • clang-Wthead-safety编译代码

    代码示例

    ```cpp

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

  1. **编译方式:`-Wthread-safety `**<br />`clang -c -Wthread-safety example.cpp`
  2. <a name="NzUsg"></a>
  3. ## 代码解析
  4. `GUARDED_BY(mu)`:对于`balance`读写之前,必须对`mu`上锁,保证增减操作原子性。<br />`REQUIRES(mu)`: 表示调用函数前“【假定】保证对`mu`上锁,此为前提”,故函数内操作`balance`是线程安全的。<br />在`withdraw`中报错`WARNING! Failed to unlock mu`.说明`REQUIRES`可检测锁的解锁操作;<br />在24行报错`WARNING! Calling withdrawImpl() requires locking b.mu.`,说明线程分析可理解不同的锁。
  5. ![](https://cdn.nlark.com/yuque/0/2021/jpeg/2728415/1616403522090-ce9873af-7770-4834-aad3-db34742380a8.jpeg#align=left&display=inline&height=320&margin=%5Bobject%20Object%5D&originHeight=320&originWidth=696&size=0&status=done&style=none&width=696)
  6. ![](https://cdn.nlark.com/yuque/0/2021/jpeg/2728415/1616403544823-f13eebfd-e000-44a6-b8f5-e12497743bf6.jpeg#align=left&display=inline&height=330&margin=%5Bobject%20Object%5D&originHeight=515&originWidth=1440&size=0&status=done&style=none&width=923)
  7. <a name="mqabJ"></a>
  8. # 基本概念:功能
  9. 线程安全分析提供**了一种用保护资源的能力**。资源可以是数据成员,也可以是提供对底层资源访问的函数/方法。分析确保调用线程不能访问资源(即调用函数,或读写数据),除非它有能力这样做。<br />能力与已命名的c++对象相关联,这些对象声明特定的方法来获取和释放能力。对象的名称用于识别功能,最常见的例子是互斥锁。例如,如果`mu`是一个互斥锁,那么调用`mu. lock()`会使调用线程获得访问受mu保护的数据的能力。类似地,调用`mu.Unlock()`会释放该能力。<br />一个线程的能力可共享、可独占。 比如说多读单写模式,就包含了读能力共享,写能力独占。<br />程序运行的任意时刻,线程均有用一组特定的能力,可访问给定资源。线程仅能与其他线程互相释放能力或获取能力,而不能拷贝能力,亦或是摧毁。【注解】假设底层实现以适当方式切换,而不知用于获取和释放的具体机制。
  10. <a name="0tRD9"></a>
  11. # 参考指南
  12. 线程安全分析使用属性来声明线程约束。属性必须附加到命名声明,如类、方法和数据成员。强烈建议用户为各种属性定义宏;示例定义可以在下面的`mutex.h`中找到。下面的文档假设使用宏。<br />这些属性只控制线程安全分析做出的假设和它发出的警告。它们不会影响生成的代码或运行时的行为。
  13. <a name="ROJhW"></a>
  14. # 属性示例
  15. <a name="kxmA3"></a>
  16. ## `GUARDED_BY(c)` and `PT_GUARDED_BY(c)` : 数据成员
  17. `GUARDED_BY`是数据成员上的一个属性,它声明数据成员受到给定能力的保护。对数据的读操作需要共享访问,而写操作需要排他访问。<br />`PT_GUARDED_BY`与上述,但用于指针和智能指针。数据成员本身没有约束,但是它所指向的数据受到给定能力的保护。
  18. > `GUARDED_BY(mu)`能力:
  19. > 能力为`mu`,为数据成员`p1`提供`mu`的保护,即对数据的读操作需要共享访问,而写操作需要排他访问。
  20. ```cpp
  21. Mutex mu;
  22. int p1 GUARDED_BY(mu);
  23. int *p2 PT_GUARDED_BY(mu);
  24. unique_ptr<int> p3 PT_GUARDED_BY(mu);
  25. void test() {
  26. p1 = 0; // Warning!
  27. *p2 = 42; // Warning!
  28. p2 = new int; // OK.
  29. *p3 = 42; // Warning!
  30. p3.reset(new int); // OK.
  31. }

REQUIRES(…), REQUIRES_SHARED(…) :函数或方法

REQUIRES是函数或方法上的一个属性,它声明调用线程必须独占访问给定的能力。可以指定多个功能。功能必须在函数进入前持有,退出时后必须持有。
REQUIRES_SHARED 类似,但仅支持共享访问

REQUIRES(mu1, mu2)能力解释:
能力为mu1,mu2,在函数**进入前**持有,退出时后必须持有

  1. Mutex mu1, mu2;
  2. int a GUARDED_BY(mu1);
  3. int b GUARDED_BY(mu2);
  4. void foo() REQUIRES(mu1, mu2) {
  5. a = 0;
  6. b = 0;
  7. }
  8. void test() {
  9. mu1.Lock();
  10. foo(); // Warning! Requires mu2.
  11. mu1.Unlock();
  12. }

ACQUIRE(…), ACQUIRE_SHARED(…), RELEASE(…),RELEASE_SHARED(…),RELEASE_GENERIC(…)

适用于函数或方法

有参数

ACQUIREACQUIRE_SHARED是函数或方法上的属性,声明函数获得能力,但不释放它。该能力进入前不持有,但在退出后持有(SHARED区别同上)。
RELEASE则相反,声明函数释放能力,即能力在进入前持有,退出前释放

  • SHARED:共享
  • GENERIC:支持独占与共享

    ACQUIRE(mu)能力解释: 能力为mu,在进入lockAndInit后持有,退出后持有

  1. Mutex mu;
  2. MyClass myObject GUARDED_BY(mu);
  3. void lockAndInit() ACQUIRE(mu) {
  4. mu.Lock();
  5. myObject.init();
  6. }
  7. void cleanupAndUnlock() RELEASE(mu) {
  8. myObject.cleanup();
  9. } // Warning! Need to unlock mu.
  10. void test() {
  11. lockAndInit();
  12. myObject.doSomething();
  13. cleanupAndUnlock();
  14. myObject.doSomething(); // Warning, mu is not locked.
  15. }

无参数

如果没有传递参数给ACQUIRERELEASE,则假定参数为this,分析不会检查函数体。这个模式的目的是让那些将锁细节隐藏在抽象接口后面的类使用,代码如下:

  1. template <class T>
  2. class CAPABILITY("mutex") Container {
  3. /* 指定类的对象可作为能力使用 */
  4. private:
  5. Mutex mu;
  6. T* data;
  7. public:
  8. // Hide mu from public interface.
  9. void Lock() ACQUIRE() { mu.Lock(); }
  10. void Unlock() RELEASE() { mu.Unlock(); }
  11. T& getElem(int i) { return data[i]; }
  12. };
  13. void test() {
  14. Container<int> c;
  15. c.Lock();
  16. int i = c.getElem(0);
  17. c.Unlock();
  18. }

需配合CAPABILITY使用,将Container转为能力

EXCLUDES(…): 函数或方法,用于防止死锁(针对不可重入锁)

exclude是函数或方法上的一个属性,声明调用者在调用之前不能持有给定能力。此注释用于防止死锁。许多互斥锁的实现都不是可重入的,所以如果函数第二次获得互斥锁,就会发生死锁。

EXCLUDES(mu):线程在调用clear时不能持有mu,否则会发生死锁


  1. Mutex mu;
  2. int a GUARDED_BY(mu);
  3. void clear() EXCLUDES(mu) {
  4. mu.Lock();
  5. a = 0;
  6. mu.Unlock();
  7. }
  8. void reset() {
  9. mu.Lock();
  10. clear(); // Warning! Caller cannot hold 'mu'.
  11. mu.Unlock();
  12. }
  13. /* 注释11与13行,则无警告 */

require不同,exclude是可选的。如果属性缺失,分析将不会发出警告,这在某些情况下可能导致假否定。【在Negative Capabilities中继续讨论该问题】

NO_THREAD_SAFETY_ANALYSIS:函数或方法,关闭线程安全检查,放在定义中!

NO_THREAD_SAFETY_ANALYSIS是函数或方法的一个属性,它关闭该方法的线程安全检查。
它为(1)故意使用线程不安全的函数(2)线程安全的函数提供了一个安全出口,但分析起来太复杂而无法理解。

  1. class Counter {
  2. Mutex mu;
  3. int a GUARDED_BY(mu);
  4. void unsafeIncrement() NO_THREAD_SAFETY_ANALYSIS { a++; }
  5. };
  6. /* 由unsateIncrement() 被该属性绑定,故编译时无警告。 */

与其他属性不同,NO_THREAD_SAFETY_ANALYSIS不是函数接口的一部分,因此应该放在函数定义中(在.cc或.cpp文件中),而不是放在函数声明中(在头文件中)。

RETURN_CAPABILITY(c): 函数或方法,注释返回互斥对象的getter方法

RETURN_CAPABILITY是函数或方法的一个属性,它声明函数返回对给定能力的引用。它用于注释返回互斥对象的getter方法。

  1. class MyClass {
  2. private:
  3. Mutex mu;
  4. int a GUARDED_BY(mu);
  5. public:
  6. Mutex* getMu() RETURN_CAPABILITY(mu) { return &mu; }
  7. // analysis knows that getMu() == mu
  8. void clear() REQUIRES(getMu()) { a = 0; }
  9. };

ACQUIRED_BEFORE(…), ACQUIRED_AFTER(…) : 类成员声明,指定顺序,

未生效,需改进

ACQUIRED_BEFOREACQUIRED_AFTER是施加于类成员声明的属性,特别是互斥锁或其他功能的声明。为了防止死锁,这些声明强制按照特定的顺序获取互斥锁

  1. Mutex m1;
  2. Mutex m2 ACQUIRED_AFTER(m1);
  3. // Alternative declaration
  4. // Mutex m2;
  5. // Mutex m1 ACQUIRED_BEFORE(m2);
  6. void foo() {
  7. m2.Lock();
  8. m1.Lock(); // Warning! m2 must be acquired after m1.
  9. m1.Unlock();
  10. m2.Unlock();
  11. }

经测试,上述代码并不触发warning,很奇怪,未找到原因;请找到原因的知会我一下,谢谢!

CAPABILITY(<string>)

CAPABILITY是类的一个属性,它指定类的对象可以作为能力使用。string参数指定错误消息中的功能类型,例如:“mutex”。

  1. #include "mutex.h"
  2. template <class T>
  3. class CAPABILITY("mutex") Mutex_lock {
  4. private:
  5. Mutex mu;
  6. T* data;
  7. public:
  8. void Lock() ACQUIRE() { mu.Lock(); }
  9. //'acquire_capability' attribute without capability arguments refers to 'this',
  10. //but 'Mutex_lock' isn't annotated with 'capability' or 'scoped_lockable' attribute
  11. void Unlock() RELEASE() { mu.Unlock(); } //'release_capability' attribute without capability arguments refers to 'this', but 'Mutex_lock' isn't annotated with 'capability' or 'scoped_lockable' attribute
  12. T& getElem(int i) { return data[i]; }
  13. };
  14. void test() {
  15. Mutex_lock<int> c;
  16. c.Lock();
  17. int i = c.getElem(0);
  18. c.Unlock();
  19. }

如上测试,若删除CAPABILITY("mutex"),则会在L10和L11报warning,意思为:
类未用capabilityscoped_lockable注解,故this非能力参数,不能作为属性参数使用

SCOPED_CAPABILITY:类,RAII锁

SCOPED_CAPABILITY是实现RAII-style锁定的类的一个属性,在这些类中,功能在构造函数中获得,在析构函数中释放。
这样的类需要特殊的处理,因为构造函数和析构函数通过不同的名称引用功能

TRY_ACQUIRE(<bool>, …), TRY_ACQUIRE_SHARED(<bool>, …)

它们是函数或方法上的属性,试图获得给定的功能,并返回一个指示成功或失败的布尔值。第一个参数必须为true或false,以指定哪个返回值表示成功,其余参数以与ACQUIRE相同的方式(持有不释放)解释。

  1. Mutex mu;
  2. int a GUARDED_BY(mu);
  3. void foo() {
  4. bool success = mu.TryLock();
  5. a = 0; // Warning, mu is not locked.
  6. if (success) {
  7. a = 0; // Ok.
  8. mu.Unlock();
  9. } else {
  10. a = 0; // Warning, mu is not locked.
  11. }
  12. }

ASSERT_CAPABILITY(…) and ASSERT_SHARED_CAPABILITY(…)

这些是函数或方法上的属性,它断言调用线程已经拥有给定的能力。例如,通过执行一个运行时测试,并在能力未被持有时终止。这种注释的存在会导致分析假定在调用注释的函数后保留该功能。

警告标志

-Wthread-safety: Umbrella flag which turns on the following three:

  • -Wthread-safety-attributes: 检查属性语法的完整性
  • -Wthread-safety-analysis: 核心分析法
  • -Wthread-safety-precise: 要求互斥量表达式精确匹配。对于有很多别名的代码,可以禁用此警告。
  • -Wthread-safety-reference: 检查当被保护的成员通过引用传递。


Negative Capabilities

线程安全分析旨在防止竞争条件和死锁。GUARDED_BYREQUIRES属性通过确保在读取或写入受保护的数据之前持有某个能力来防止竞争条件,而EXCLUDES属性通过确保没有持有互斥锁来防止死锁。
然而,EXCLUDES是一个可选属性,不提供与要求相同的安全保证。特别是:

  • 获得某种能力的函数并不一定要排除它
  • 如果A函数调用了B函数,B函数排除了某个能力,A函数就不能递归排除该功能。

因此,EXCLUDES很容易产生错误否定:

  1. class Foo {
  2. Mutex mu;
  3. void foo() {
  4. mu.Lock();
  5. bar(); // No warning.
  6. baz(); // No warning.
  7. mu.Unlock();
  8. }
  9. void bar() { // No warning. (Should have EXCLUDES(mu)).
  10. mu.Lock();
  11. // ...
  12. mu.Unlock();
  13. }
  14. void baz() {
  15. bif(); // No warning. (Should have EXCLUDES(mu)).
  16. }
  17. void bif() EXCLUDES(mu);
  18. };
  19. /* 调用baz()应该会触发warning,但实际并没有,*/
  20. /* 故baz()调用了bif(),bif()排除了mu,baz()并没有拍出mu */

Negative requirements

为EXCLUDES提供了更强的安全保证

采用REQUIRES属性,链接,表示不应拥有某种能力

  1. class FooNeg {
  2. Mutex mu;
  3. void foo() REQUIRES(!mu) { // foo() now requires !mu.
  4. mu.Lock();
  5. bar();
  6. baz();
  7. mu.Unlock();
  8. }
  9. void bar() {
  10. mu.Lock(); // WARNING! Missing REQUIRES(!mu).
  11. // ...
  12. mu.Unlock();
  13. }
  14. void baz() {
  15. bif(); // warning: calling function 'bif' requires holding '!mu'
  16. }
  17. void bif() REQUIRES(!mu);
  18. };

Negative requirements是一个实验性的特性,默认情况下是关闭的,因为它会在现有代码中产生许多警告。它可以通过传递-Wthread-safety-negative来启用。

常见问题

Q.Should I put attributes in the header file, or in the .cc/.cpp/.cxx file?
(A) Attributes are part of the formal interface of a function, and should always go in the header, where they are visible to anything that includes the header. Attributes in the .cpp file are not visible outside of the immediate translation unit, which leads to false negatives and false positives.

Q.“Mutex is not locked on every path through here?” What does that mean?
A.See No conditionally held locks., below.

已知限制(自查,比较简单)

https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#known-limitations

mutex.h

  1. #ifndef THREAD_SAFETY_ANALYSIS_MUTEX_H
  2. #define THREAD_SAFETY_ANALYSIS_MUTEX_H
  3. // Enable thread safety attributes only with clang.
  4. // The attributes can be safely erased when compiling with other compilers.
  5. #if defined(__clang__) && (!defined(SWIG))
  6. #define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
  7. #else
  8. #define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
  9. #endif
  10. #define CAPABILITY(x) \
  11. THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
  12. #define SCOPED_CAPABILITY \
  13. THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
  14. #define GUARDED_BY(x) \
  15. THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
  16. #define PT_GUARDED_BY(x) \
  17. THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
  18. #define ACQUIRED_BEFORE(...) \
  19. THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
  20. #define ACQUIRED_AFTER(...) \
  21. THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
  22. #define REQUIRES(...) \
  23. THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
  24. #define REQUIRES_SHARED(...) \
  25. THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
  26. #define ACQUIRE(...) \
  27. THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
  28. #define ACQUIRE_SHARED(...) \
  29. THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
  30. #define RELEASE(...) \
  31. THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
  32. #define RELEASE_SHARED(...) \
  33. THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
  34. #define RELEASE_GENERIC(...) \
  35. THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__))
  36. #define TRY_ACQUIRE(...) \
  37. THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
  38. #define TRY_ACQUIRE_SHARED(...) \
  39. THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
  40. #define EXCLUDES(...) \
  41. THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
  42. #define ASSERT_CAPABILITY(x) \
  43. THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
  44. #define ASSERT_SHARED_CAPABILITY(x) \
  45. THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
  46. #define RETURN_CAPABILITY(x) \
  47. THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
  48. #define NO_THREAD_SAFETY_ANALYSIS \
  49. THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
  50. // Defines an annotated interface for mutexes.
  51. // These methods can be implemented to use any internal mutex implementation.
  52. class CAPABILITY("mutex") Mutex {
  53. public:
  54. // Acquire/lock this mutex exclusively. Only one thread can have exclusive
  55. // access at any one time. Write operations to guarded data require an
  56. // exclusive lock.
  57. void Lock() ACQUIRE();
  58. // Acquire/lock this mutex for read operations, which require only a shared
  59. // lock. This assumes a multiple-reader, single writer semantics. Multiple
  60. // threads may acquire the mutex simultaneously as readers, but a writer
  61. // must wait for all of them to release the mutex before it can acquire it
  62. // exclusively.
  63. void ReaderLock() ACQUIRE_SHARED();
  64. // Release/unlock an exclusive mutex.
  65. void Unlock() RELEASE();
  66. // Release/unlock a shared mutex.
  67. void ReaderUnlock() RELEASE_SHARED();
  68. // Generic unlock, can unlock exclusive and shared mutexes.
  69. void GenericUnlock() RELEASE_GENERIC();
  70. // Try to acquire the mutex. Returns true on success, and false on failure.
  71. bool TryLock() TRY_ACQUIRE(true);
  72. // Try to acquire the mutex for read operations.
  73. bool ReaderTryLock() TRY_ACQUIRE_SHARED(true);
  74. // Assert that this mutex is currently held by the calling thread.
  75. void AssertHeld() ASSERT_CAPABILITY(this);
  76. // Assert that is mutex is currently held for read operations.
  77. void AssertReaderHeld() ASSERT_SHARED_CAPABILITY(this);
  78. // For negative capabilities.
  79. const Mutex& operator!() const { return *this; }
  80. };
  81. // Tag types for selecting a constructor.
  82. struct adopt_lock_t {} inline constexpr adopt_lock = {};
  83. struct defer_lock_t {} inline constexpr defer_lock = {};
  84. struct shared_lock_t {} inline constexpr shared_lock = {};
  85. // MutexLocker is an RAII class that acquires a mutex in its constructor, and
  86. // releases it in its destructor.
  87. class SCOPED_CAPABILITY MutexLocker {
  88. private:
  89. Mutex* mut;
  90. bool locked;
  91. public:
  92. // Acquire mu, implicitly acquire *this and associate it with mu.
  93. MutexLocker(Mutex *mu) ACQUIRE(mu) : mut(mu), locked(true) {
  94. mu->Lock();
  95. }
  96. // Assume mu is held, implicitly acquire *this and associate it with mu.
  97. MutexLocker(Mutex *mu, adopt_lock_t) REQUIRES(mu) : mut(mu), locked(true) {}
  98. // Acquire mu in shared mode, implicitly acquire *this and associate it with mu.
  99. MutexLocker(Mutex *mu, shared_lock_t) ACQUIRE_SHARED(mu) : mut(mu), locked(true) {
  100. mu->ReaderLock();
  101. }
  102. // Assume mu is held in shared mode, implicitly acquire *this and associate it with mu.
  103. MutexLocker(Mutex *mu, adopt_lock_t, shared_lock_t) REQUIRES_SHARED(mu)
  104. : mut(mu), locked(true) {}
  105. // Assume mu is not held, implicitly acquire *this and associate it with mu.
  106. MutexLocker(Mutex *mu, defer_lock_t) EXCLUDES(mu) : mut(mu), locked(false) {}
  107. // Release *this and all associated mutexes, if they are still held.
  108. // There is no warning if the scope was already unlocked before.
  109. ~MutexLocker() RELEASE() {
  110. if (locked)
  111. mut->GenericUnlock();
  112. }
  113. // Acquire all associated mutexes exclusively.
  114. void Lock() ACQUIRE() {
  115. mut->Lock();
  116. locked = true;
  117. }
  118. // Try to acquire all associated mutexes exclusively.
  119. bool TryLock() TRY_ACQUIRE(true) {
  120. return locked = mut->TryLock();
  121. }
  122. // Acquire all associated mutexes in shared mode.
  123. void ReaderLock() ACQUIRE_SHARED() {
  124. mut->ReaderLock();
  125. locked = true;
  126. }
  127. // Try to acquire all associated mutexes in shared mode.
  128. bool ReaderTryLock() TRY_ACQUIRE_SHARED(true) {
  129. return locked = mut->ReaderTryLock();
  130. }
  131. // Release all associated mutexes. Warn on double unlock.
  132. void Unlock() RELEASE() {
  133. mut->Unlock();
  134. locked = false;
  135. }
  136. // Release all associated mutexes. Warn on double unlock.
  137. void ReaderUnlock() RELEASE() {
  138. mut->ReaderUnlock();
  139. locked = false;
  140. }
  141. };
  142. #ifdef USE_LOCK_STYLE_THREAD_SAFETY_ATTRIBUTES
  143. // The original version of thread safety analysis the following attribute
  144. // definitions. These use a lock-based terminology. They are still in use
  145. // by existing thread safety code, and will continue to be supported.
  146. // Deprecated.
  147. #define PT_GUARDED_VAR \
  148. THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_var)
  149. // Deprecated.
  150. #define GUARDED_VAR \
  151. THREAD_ANNOTATION_ATTRIBUTE__(guarded_var)
  152. // Replaced by REQUIRES
  153. #define EXCLUSIVE_LOCKS_REQUIRED(...) \
  154. THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
  155. // Replaced by REQUIRES_SHARED
  156. #define SHARED_LOCKS_REQUIRED(...) \
  157. THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
  158. // Replaced by CAPABILITY
  159. #define LOCKABLE \
  160. THREAD_ANNOTATION_ATTRIBUTE__(lockable)
  161. // Replaced by SCOPED_CAPABILITY
  162. #define SCOPED_LOCKABLE \
  163. THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
  164. // Replaced by ACQUIRE
  165. #define EXCLUSIVE_LOCK_FUNCTION(...) \
  166. THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
  167. // Replaced by ACQUIRE_SHARED
  168. #define SHARED_LOCK_FUNCTION(...) \
  169. THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
  170. // Replaced by RELEASE and RELEASE_SHARED
  171. #define UNLOCK_FUNCTION(...) \
  172. THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
  173. // Replaced by TRY_ACQUIRE
  174. #define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
  175. THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
  176. // Replaced by TRY_ACQUIRE_SHARED
  177. #define SHARED_TRYLOCK_FUNCTION(...) \
  178. THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
  179. // Replaced by ASSERT_CAPABILITY
  180. #define ASSERT_EXCLUSIVE_LOCK(...) \
  181. THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
  182. // Replaced by ASSERT_SHARED_CAPABILITY
  183. #define ASSERT_SHARED_LOCK(...) \
  184. THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__))
  185. // Replaced by EXCLUDE_CAPABILITY.
  186. #define LOCKS_EXCLUDED(...) \
  187. THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
  188. // Replaced by RETURN_CAPABILITY
  189. #define LOCK_RETURNED(x) \
  190. THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
  191. #endif // USE_LOCK_STYLE_THREAD_SAFETY_ATTRIBUTES
  192. #endif // THREAD_SAFETY_ANALYSIS_MUTEX_H