Bootstrap

Clang ThreadSafetyAnalysis Clang 线程安全分析

介绍

Clang 线程安全分析是一个 C++ 语言扩展,它对 代码中的潜在争用条件。分析是完全静态的(即编译时没有运行时开销分析仍然是正在积极开发中,但它已经足够成熟,可以部署在工业环境。它由 Google 与 CERT/SEI,并在 Google 的内部代码库中广泛使用。

线程安全分析的工作方式与多线程的类型系统非常相似 程序。除了声明数据类型(例如 int、flout、 等),程序员可以(可选地)声明对该数据的访问方式 在多线程环境中进行控制。例如,如果由 mutex 保护,则分析将在 一段代码在没有首先锁定的情况下读取或写入 。 同样,如果存在只能由 GUI 线程,则分析将在其他线程调用这些 例程。

一个简单的例子

#include <mutex>
class Mutex {
public:
  void Lock() { }
  void Unlock() { }
};

#define GUARDED_BY(x)  //GUARDED_BY 属性声明线程必须先锁定 mu 才能读取或写入 balance,从而确保增量和减量操作是原子的
#define REQUIRES(x)    //REQUIRES  声明调用线程在调用 withdrawImpl 之前必须锁定 mu。


class BankAccount {
private:
  Mutex mu;
  int   balance GUARDED_BY(mu);

    // brief : 存款实现
  void depositImpl(int amount) {
    balance += amount;       // WARNING! Cannot write balance without locking mu. 因为REQUIRES
  }


    // brief : 取钱实现
  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。分析会理解这是两个不同的对象中的两个单独的互斥锁。

最后,withdraw() 方法中有一个警告,因为它无法解锁 mu。每个锁都必须有相应的解锁,分析将检测双重锁定和双重解锁。允许函数获取锁而不释放它(反之亦然),但必须这样注释(使用 ACQUIRE/RELEASE)。

Capabilities的基本概念

线程安全分析提供了一种通过能力保护资源的方法。(资源可以是数据成员,也可以是提供对某些底层资源的访问的函数/方法)该分析可确保调用线程无法访问资源(即调用函数或读取/写入数据),除非它具有这样做的能力。
功能与命名的 C++ 对象相关联,这些对象声明了获取和释放功能的特定方法。对象的名称用于标识功能。最常见的示例是互斥锁。例如,如果 mu 是互斥锁,则调用 mu.Lock() 会导致调用线程获得访问受 mu 保护的数据的能力。同样,调用 mu.Unlock() 会释放该能力。

线程可以独占或共享地持有能力。独占能力一次只能由一个线程持有,而共享能力可以同时由多个线程持有。此机制强制执行多读取器、单写入器模式。对受保护数据的写入操作需要独占访问,而读取操作仅需要共享访问。

在程序执行期间的任何给定时刻,线程都持有一组特定的能力(例如,它已锁定的互斥锁集)。这些能力就像允许线程访问给定资源的密钥或令牌。就像物理安全密钥一样,线程不能复制能力,也不能销毁能力。线程只能将能力释放给另一个线程,或从另一个线程获取能力。注释故意不考虑用于获取和释放能力的确切机制;它假设底层实现(例如互斥锁实现)以适当的方式进行交接。

在程序执行的给定点,给定线程实际持有的能力集是一个运行时概念。静态分析通过计算该集合的近似值(称为能力环境)来工作。能力环境针对每个程序点进行计算,并描述静态地知道在该特定点保留或不保留的能力集。此环境是线程在运行时实际保留的全套能力的保守近似值。

Capabilities参考指南

线程安全分析使用属性来声明线程约束。属性必须附加到命名声明,例如类、方法和数据成员。强烈建议用户为各种属性定义宏;示例定义可以在下面的 mutex.h 中找到。以下文档假定使用宏。
这些属性仅控制线程安全分析做出的假设及其发出的警告。它们不会影响生成的代码或运行时行为。
由于历史原因,先前版本的线程安全使用了非常以锁为中心的宏名称。这些宏后来被重命名以适应更通用的功能模型。先前的名称仍在使用,并将在适当的位置在之前的标签下提及。

GUARDED_BY© PT_GUARDED_BY©

GUARDED_BY 是数据成员的一个属性,声明数据成员受给定功能的保护。对数据的读取操作需要共享访问,而写入操作需要独占访问。
PT_GUARDED_BY 类似,但旨在用于指针和智能指针。数据成员本身没有约束,但它指向的数据受到给定功能的保护。

#include "mutex.h"
#include <memory>

Mutex mu;
int *p1                  GUARDED_BY(mu);
int *p2                  PT_GUARDED_BY(mu);
std::unique_ptr<int> p3  PT_GUARDED_BY(mu); 

int main() {

  p1 = 0;             // Warning!
  *p2 = 42;           // Warning!
  p2 = new int;       // OK.
  *p3 = 42;           // Warning!
  p3.reset(new int);  // OK.

}

在这里插入图片描述

REQUIRES(…) REQUIRES_SHARED(…)

Previously: EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED

REQUIRES 是函数或方法上的一个属性,它声明调用线程必须对给定的功能具有独占访问权限。可以指定多个功能。这些功能必须在函数入口处保留,并且在退出时仍必须保留。

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

请添加图片描述

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

Previously: EXCLUSIVE_LOCK_FUNCTION, SHARED_LOCK_FUNCTION, UNLOCK_FUNCTION

ACQUIREACQUIRE_SHARED 是函数或方法上的属性,用于声明函数获取功能但不释放该功能。给定的功能不得在进入时保留,并且将在退出时保留(对于 ACQUIRE 是独有的,对于 ACQUIRE_SHARED 是共享的)。

RELEASE、RELEASE_SHAREDRELEASE_GENERIC 声明函数释放给定的功能。功能必须在进入时保留(RELEASE 独占、RELEASE_SHARED 共享、RELEASE_GENERIC 独占或共享),退出时将不再保留。


#include "mutex.h"
#include <memory>

class MyClass {
public:
  void init() EXCLUSIVE_LOCK_FUNCTION();
  void cleanup() UNLOCK_FUNCTION();
  void doSomething() EXCLUSIVE_LOCKS_REQUIRED(mu);
};

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.
}

如果没有参数传递给 ACQUIRERELEASE,则参数被假定为 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();
}

EXCLUDES(…)

Previously: LOCKS_EXCLUDED

EXCLUDES 是函数或方法上的一个属性,声明调用者不得拥有给定的功能。此注释用于防止死锁。许多互斥锁实现都不是可重入的,因此如果函数再次获取互斥锁,则可能会发生死锁。

#include "mutex.h"

Mutex mu;
int a GUARDED_BY(mu);

void clear()EXCLUDES(mu)
{
    mu.Lock();
    a = 0;
    mu.Unlock();
}

void reset()
{
    mu.Lock();
    clear();// warning: cannot call function 'clear' while mutex 'mu' is held [-Wthread-safety-analysis]
    mu.Unlock();
}

REQUIRES 不同,EXCLUDES 是可选的。如果属性缺失,分析不会发出警告,这在某些情况下可能会导致误报。此问题在负面功能中进一步讨论。

NO_THREAD_SAFETY_ANALYSIS

NO_THREAD_SAFETY_ANALYSIS 是函数或方法上的一个属性,用于关闭该方法的线程安全检查。
它为以下函数提供了一个逃生出口:
(1) 故意线程不安全,或 (2) 线程安全,但过于复杂,无法通过分析理解。(2) 的原因将在下文的已知限制中描述。

class Counter {
  Mutex mu;
  int a GUARDED_BY(mu);

  void unsafeIncrement() NO_THREAD_SAFETY_ANALYSIS { a++; }
}

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

RETURN_CAPABILITY©

Previously: LOCK_RETURNED

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

#include "mutex.h"

class MyClass {
private:
  Mutex mu;
  int a GUARDED_BY(mu);

public:
  Mutex* getMu() RETURN_CAPABILITY(mu) { return &mu; }

  // analysis knows that getMu() == mu
  void clear() REQUIRES(getMu()) { a = 0; }
};

ACQUIRED_BEFORE(…), ACQUIRED_AFTER(…)

ACQUIRED_BEFOREACQUIRED_AFTER 是成员声明的属性,特别是互斥锁或其他功能的声明。这些声明强制执行必须按照特定顺序获取互斥锁,以防止死锁。

#include "mutex.h"

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

CAPABILITY(<string>)

Previously: LOCKABLE
CAPABILITY 是类的一个属性,它指定类的对象可用作功能。字符串参数指定错误消息中的功能类型,例如“mutex”。

请参阅上面给出的 Container 示例或 mutex.h 中的 Mutex 类。

SCOPED_CAPABILITY

SCOPED_CAPABILITY 是实现 RAII 样式锁定的类的一个属性,其中功能在构造函数中获取,并在析构函数中释放。此类类需要特殊处理,因为构造函数和析构函数通过不同的名称引用功能;请参阅下面 mutex.h 中的 MutexLocker 类。

作用域功能被视为在构造时隐式获取并在析构时释放的功能。它们与构造函数或函数上线程安全属性中命名的一组(常规)功能相关联,这些功能通过值返回(使用 C++17 保证的复制省略)。其他成员函数上的获取类型属性被视为适用于该组关联功能,而 RELEASE 则意味着函数会以任何模式释放所有关联功能。

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

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.
  }
}

ASSERT_CAPABILITY(…) ASSERT_SHARED_CAPABILITY(…)

Previously: ASSERT_EXCLUSIVE_LOCK, ASSERT_SHARED_LOCK
这些是函数或方法的属性,用于断言调用线程已拥有给定的功能,例如通过执行运行时测试并在未拥有该功能时终止。
此注释的存在会导致分析假设在调用注释函数后拥有该功能。有关示例用法,请参阅下面的 mutex.h。

GUARDED_VAR PT_GUARDED_VAR

这些属性的使用已被弃用。

Warning flags

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

    • -Wthread-safety-attributes: Semantic checks for thread safety attributes. //线程安全属性的语义检查。

    • -Wthread-safety-analysis: The core analysis.

    • -Wthread-safety-precise: Requires that mutex expressions match precisely. //要求互斥表达式精确匹配。

      This warning can be disabled for code which has a lot of aliases. //对于具有大量别名的代码可以禁用此警告。

    • -Wthread-safety-reference: Checks when guarded members are passed by reference. //检查受保护成员何时通过引用传递。
      Negative Capabilities是一项实验性功能,可通过以下方式启用:

  • -Wthread-safety-negative: Negative capabilities. Off by default.

Negative Capabilities

线程安全分析旨在防止竞争条件和死锁。GUARDED_BY 和 REQUIRES 属性可确保在读取或写入受保护数据之前保留某种能力,从而防止竞争条件;
EXCLUDES 属性可确保不保留互斥锁,从而防止死锁。

然而,EXCLUDES 是可选属性,并不提供与 REQUIRES 相同的安全保证。具体来说:

  • A function which acquires a capability does not have to exclude it.

  • A function which calls a function that excludes a capability does not have transitively exclude that capability.、

  • 获得某项能力的函数不必排除该能力。

  • 调用排除某项能力的函数的函数不必间接排除该能力。

作为结果,EXCLUDES很容易产生 false negatives:


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

Negative requirements 要求是替代的 EXCLUDES,可提供更强的安全保障。否定要求使用 REQUIRES 属性与 ! 运算符结合使用,以指示不应保留某项功能。

例如,使用 REQUIRES(!mu) 而不是 EXCLUDES(mu) 将产生相应的警告:


class FooNeg{

	Mutex mu;
	
	void foo() REQUIRES(!mu){ //foo() now require !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);
	
};

常见问题


Q: 我应该把属性放在头文件中,还是放在 .cc/.cpp/.cxx 文件中?
(A): 属性是函数正式接口的一部分,应始终放在标头中,这样它们对包含标头的任何程序都可见。.cpp 文件中的属性在直接翻译单元之外不可见,这会导致误判和误判。

Q: “这里的每条路径上的互斥锁都没有被锁定?”这是什么意思?
(A): 请参阅下文“No conditionally held locks.”。

Known Limitations


Lexical scope

线程安全属性包含普通的 C++ 表达式,因此遵循普通的 C++ 作用域规则。具体而言,这意味着互斥体和其他功能必须先声明,然后才能在属性中使用。在单个类中,声明前使用是可以的,因为属性与方法体同时解析。(C++ 将方法体的解析延迟到类的末尾。)但是,不允许在类之间使用声明前使用,如下所示

class Foo;

class Bar {
  void bar(Foo* f) REQUIRES(f->mu);  // Error: mu undeclared.
};

class Foo {
  Mutex mu;
};

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 方法,它仅用于线程安全分析。

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

No checking inside constructors and destructors.

目前,分析不会对构造函数或析构函数进行任何检查。换句话说,每个构造函数和析构函数都被视为已使用 NO_THREAD_SAFETY_ANALYSIS 注释。原因是在初始化期间,通常只有一个线程可以访问正在初始化的对象,因此初始化受保护成员而不获取任何锁是安全的(也是常见的做法)。析构函数也是如此。

理想情况下,分析将允许初始化正在初始化或销毁的对象内的受保护成员,同时仍然对其他所有内容实施通常的访问限制。然而,这在实践中很难实施,因为在基于指针的复杂数据结构中,很难确定封闭对象拥有哪些数据。

No inlining.

线程安全分析严格地在过程内进行,就像普通的类型检查一样。它仅依赖于函数声明的属性,并且不会尝试内联任何方法调用。因此,以下代码将不起作用:

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
}

MutexUnlocker 类旨在与 mutexLocker 类相对应,后者在 mutex.h 中定义。但是,它不起作用,因为分析不知道 munl.mu == mutex。SCOPED_CAPABILITY 属性处理 MutexLocker 的别名,但只针对该特定模式。

ACQUIRED_BEFORE(…) 和 ACQUIRED_AFTER(…) 支持仍处于试验阶段。
ACQUIRED_BEFORE(…) 和 ACQUIRED_AFTER(…) 目前正在 -Wthread-safety-beta 标志下进行开发。

mutex.h

线程安全分析可以与任何线程库一起使用,但它要求将线程 API 包装在具有适当注释的类和方法中。以下代码提供了 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) {}

  // Same as constructors, but without tag types. (Requires C++17 copy elision.)
  static MutexLocker Lock(Mutex *mu) ACQUIRE(mu) {
    return MutexLocker(mu);
  }

  static MutexLocker Adopt(Mutex *mu) REQUIRES(mu) {
    return MutexLocker(mu, adopt_lock);
  }

  static MutexLocker ReaderLock(Mutex *mu) ACQUIRE_SHARED(mu) {
    return MutexLocker(mu, shared_lock);
  }

  static MutexLocker AdoptReaderLock(Mutex *mu) REQUIRES_SHARED(mu) {
    return MutexLocker(mu, adopt_lock, shared_lock);
  }

  static MutexLocker DeferLock(Mutex *mu) EXCLUDES(mu) {
    return MutexLocker(mu, defer_lock);
  }

  // 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

在这里插入图片描述

;