clang之Thread Safety Analysis

4,471 阅读17分钟

Clang 12 documentation

Clang 12 documentation包含了一系列工具,如 AddressSanitizerThreadSanitizerLeakSanitizerLibTooling等。

  1. clang之AddressSanitizer
  2. clang之MemorySanitizer
  3. clang之LeakSanitizer
  4. clang之UndefinedBehaviorSanitizer
  5. clang之Hardware-assisted-AddressSanitizer
  6. clang之SafeStack
  7. clang之ShadowCallStack
  8. clang之ThreadSanitizer
  9. clang之Thread-Safety-Analysis
  10. clang之DataFlowSanitizer

这部分是对clang文档 Clang 12 documentation Thread Safety Analysis 的翻译。仅供参考。

介绍

Clang的线程安全分析是一个C++的扩展,用于对代码中潜在的竞争条件进行预警。该分析完全是静态的(即编译期间),不会有运行时的性能损耗。该分析当前仍处于开发阶段,但是对于在部署在工业中使用而言已经足够成熟了。它是由Google公司开发的,且与CERT/SEI一起合作,已经在Google的内部代码中广泛使用了。

线程安全分析的工作方式与多线程程序中的类型系统非常类似。除了声明数据(如int、float等)的类型之外,开发者可以选择性地声明在多线程环境下如何控制该数据的访问。例如,如果foo被互斥锁mu守护着,那么一旦有代码在未使用mu进行加锁的前提下对foo进行读或者写,分析器都会抛出一个警告。类似地,如果有特定的函数只能在GUI线程中调用,那么一旦在其他线程中调用这些函数,分析器就会产生警告。

开始使用

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

这个例子验证了线程安全分析的基本概念。GUARDED_BY 编译器属性声明了:一个线程在读或写balance之前,都必须要对互斥锁mu进行加锁操作,用于确保对balance的加减操作都是原子的。类似的,REQUIRES 声明了:线程在调用 withdrawImpl 函数之前,必须要对互斥锁mu进行加锁操作。因为调用者就已经加了锁,所以在该函数内部修改balance就是安全的。

depositImpl() 函数没有 REQUIRES,所以分析器会报告一个警告。线程安全分析不是 inter-procedural的,所以对调用者的要求必须显式声明处理。在 transferFrom() 函数中也有一个警告,因为尽管该方法使用了 this->mu 加锁,它却没有使用 b.mu 来加锁。分析器能够理解:这里有两个独立的互斥锁,用于两个不同的对象。

最后,在 withdraw() 函数中有一个警告,因为并没有对互斥锁mu进行解锁操作。每一个加锁操作都必须要有一个对应的解锁操作,且分析器能够检测再次加锁和再次解锁(both double locks, and double unlocks)。一个函数可以获取一个锁,而不必去释放该锁,(或者 vice versa ),但是却必须添加相应的标记(如使用 ACQUIRE/RELEASE)。

运行分析

设置编译器标记 -Wthread-safety,执行编译即可运行该分析:

clang -c -Wthread-safety example.cpp

注意:该示例假定存在一个带有适当标注的 mutex.h,其中声明了使用哪些方法来执行加锁、解锁等操作。

基本概念:能力

这里提到的 Capability 均指的是线程安全相关的一种能力,如互斥锁。

线程安全分析器提供了使用其能力模型来保护资源的方式。该资源可以是一个数据成员,也可以是一个提供了对一些潜在资源进行访问的函数或方法。分析器能够确保调用线程在未获取指定能力的前提下,无法访问该资源(如调用函数、或读写数据)。

这些能力模型跟C++的一些特定对象关联在一起,用于声明获取和释放指定能力的特殊方法。对象的名称用于区分不同的能力。最常见的一个例子就是互斥锁mutex。例如,若mu是一个互斥锁,则调用 mu.Lock() 会使得调用线程获取访问被mu保护起来的数据的能力。类似的,调用 mu.Unlock() 则相应地释放该能力。

一个线程获取该能力的方式可以使互斥的,也可以使共享的。一个互斥的能力在同一时间只能被一个线程获取,而一个共享的能力则在同一时间可以被多个线程获取。这种机制可以实现一个多读单写的访问模式。对受保护数据的写操作要求互斥访问,而读操作则仅要求共享访问。

在程序执行的任意给定时刻,一个线程可以获取一些特定的能力(如一系列已经加锁的互斥锁)。这些的表现方式就是一些运行线程访问指定资源的key或者token。就像物理世界中的安全钥匙,一个线程无法复制这种能力,也无法销毁。一个线程仅仅能够释放该能力,给其他线程使用,或者从其他线程中获取该能力。标注内容与获取和释放能力的确切机制无关。它假定潜在的实现(如互斥锁的实现),会以合适的方式来处理这种能力的转移。

在程序运行的指定时间点,由指定线程获取的这些能力其实就是runtime的概念。静态分析通过计算这些能力集合的近似值来工作的,也叫做能力环境。能力环境是为每一个程序点来计算的,描述了在特定点静态持有或未持有的能力集合。该环境是线程在runtime时期实际持有的全部能力集合的保守近似值。

参考指南

线程安全分析器使用编译器属性来声明多线程的限制。这些属性必须要跟命名的声明(named declarations)结合起来,如类、方法和数据成员。强烈建议用户针对大量不同的编译器属性来使用宏定义,可以在下边的 mutex.h 中查看样例的定义。下文都默认使用这些宏定义。

因为历史原因,线程安全的优先版本使用了非常明显的、以锁为中心的(lock-centric)的宏定义命名。这些宏定义被重命名过,以适应更通用的能力模型。这些优先的名字依然在使用中,会在合适的场景下使用tag来明显的做提示。

GUARDED_BY(c) 和 PT_GUARDED_BY(c)

GUARDED_BY 是一个用于数据成员的属性,声明该数据成员会受到给定能力的保护。数据的读操作会要求共享访问,而写操作则要求互斥的访问。

PT_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(…), REQUIRES_SHARED(…)

以前: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(…)

以前:EXCLUSIVE_LOCK_FUNCTION, SHARED_LOCK_FUNCTION, UNLOCK_FUNCTION

ACQUIRE 是函数或方法的一个属性,声明函数获取了能力,但不释放它。调用方在进入时不得保留指定能力,在退出时会保留该能力。ACQUIRE_SHARED 是类似的。

RELEASERELEASE_SHARED 声明函数会释放给定的能力。调用方必须在如何保留该能力,而在退出时不再保留。它与指定能力是共享的或是互斥的无关。

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(…)

以前:LOCKS_EXCLUDED

EXCLUDES 是函数或方法的属性,声明了调用方必须不能持有指定能力。标注用于防止产生死锁。许多互斥锁的实现并不是可重入的,所以如果函数再次获取互斥锁就会导致死锁。

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

REQUIRES 不同的是,EXCLUDES 是可选的。如果没有该属性,分析器不会生成警告,这在一些场景下可能产生误报。这在后续的 能力误报 章节会讨论。

NO_THREAD_SAFETY_ANALYSIS

NO_THREAD_SAFETY_ANALYSIS 是函数或方法的一个属性,它会针对该方法而关闭线程安全检查。它会针对一些函数关闭检查,要么是故意的非线程安全,要么是明确的线程安全,而这些函数太过于复杂以至于分析器无法理解。对于明确的线程安全,会在下边的已知限制中详细描述。

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

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

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

RETURN_CAPABILITY(c)

以前:LOCK_RETURNED

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

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 是成员声明时的属性,特别用于互斥锁或其他能力的声明。这些声明维持一个特定的顺序:互斥锁必须要先被持有,以避免发生死锁。

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

以前:LOCKABLE

CAPABILITY 是一个用于类的属性,声明类的对象可以被用作一个能力。字符串参数指定了此种能力在错误信息中的表示,如 mutex。见上文给定的 Container 样例,或者 mutex.h中的 Mutex 类。

SCOPED_CAPABILITY

以前:SCOPED_LOCKABLE

SCOPED_CAPABILITY 是一个类的属性,用于实现 RAII 类型的锁机制:构造函数中持有能力,析构函数中释放能力。这样的类要求特殊的处理,因为构造函数和析构函数通过不同的名称来访问该能力。见下文 mutex.h 中的 MutexLocker类。

TRY_ACQUIRE(, …), TRY_ACQUIRE_SHARED(, …)

以前:EXCLUSIVE_TRYLOCK_FUNCTION, SHARED_TRYLOCK_FUNCTION

这些是函数或方法的属性,尝试去获取指定能力,返回一个布尔值表示成功或失败。第一个参数必须是true或false,以指定哪个返回值代表了成功。剩下的参数的理解方式与 ACQUIRE 一样。使用样例见下边的 mutex.h

ASSERT_CAPABILITY(…) and ASSERT_SHARED_CAPABILITY(…)

以前:ASSERT_EXCLUSIVE_LOCK, ASSERT_SHARED_LOCK

这些是函数或方法的属性,执行一个runtime测试,以查看调用线程是否已经持有指定能力。在未持有该能力时,该函数预期会执行失败(无返回值)。使用样例见下边的 mutex.h

GUARDED_VAR and PT_GUARDED_VAR

该属性已经弃用。

警告标记

  • -Wthread-safety: Umbrella flag 开启如下三种警告:
    • -Wthread-safety-attributes: 属性语法的检查。
    • -Wthread-safety-analysis: 核心分析。
    • -Wthread-safety-precise: 要求互斥锁的表达式精确匹配。该警告在有大量别名的代码中可以禁用
    • -Wthread-safety-reference: 守护的成员通过引用传递的时候进行检查。

Negative capabilities 是一个实验性的特性,可以使用如下标记来启用:

  • -Wthread-safety-negative: Negative capabilities. 默认关闭。

当新的特性和检查添加到分析器中,他们通常会带来新的警告。这些警告最初是以beta警告的形式发布并持续一段时间,之后会合并到标准分析中。

  • -Wthread-safety-beta: 新特性,默认关闭。

Negative Capabilities

线程安全分析被设计用于阻止竞争条件和死锁。GUARDED_BYREQUIRES 特性会阻止竞争条件,原理是确保:在对守护数据进行读或写操作之前都先持有该能力,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 很容易产生误报(produce 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 are an alternative EXCLUDES that provide a stronger safety guarantee. A negative requirement uses the REQUIRES attribute, in conjunction with the ! operator, to indicate that a capability should not be held.

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

class FooNeg {
  Mutex mu;

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

Negative requirements are an experimental feature which is off by default, because it will produce many warnings in existing code. It can be enabled by passing -Wthread-safety-negative.

FAQ

Q: 是否需要将属性放到头文件中,或者实现文件 .cc/.cpp/.cxx 中? (A) 属性是函数的正式接口的一部分,需要一直放在头文件中,以便于对任何包含头文件的代码都可见。.cpp 文件中的属性对外部不可见,会导致误报。

Q: “互斥锁并非在每个场景下都加锁。”这句话是什么意思? 见下文的 去掉条件锁。

已知的限制

词法作用域

线程安全分析包含了普通的C++表达式,遵循普通的C++作用域规则。特别是,意味着互斥锁和其他能力必须在使用前进行提前声明。在声明前使用(Use-before-declaration)在一个单独的类是可行的,因为属性会与方法体同时进行分析。(C++直到类文件的底部才去解析方法体。)然而,在声明前使用不允许在类之间使用,如下:

class Foo;

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

class Foo {
  Mutex mu;
};

私有的互斥锁

良好的软件工程实践规定,互斥锁应该是私有成员,因为线程安全的类使用的锁机制是类的内部实现的一部分。然而,私有的互斥锁有时候可能泄露给类的公共接口。线程安全属性遵循常见的C++访问限制,所以如果mu是c的一个私有成员,name在一个属性中访问 c.mu 会出错。

一个解决办法是使用 RETURN_CAPABILITY 属性来为私有互斥锁提供一个对外的名字,而实际避免将内部的互斥锁暴露出去。如:

class MyClass {
private:
  Mutex mu;

public:
  // For thread safety analysis only.  Does not actually return mu.
  Mutex* getMu() RETURN_CAPABILITY(mu) { return 0; }

  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方法,仅仅是对于线程安全分析有好处而已。

去掉条件锁

分析器必须有能力确认:在任意程序运行时机,一个锁是否被持有。因此,可能持有锁的一段代码,会产生一些错误的警告(如误报)。例如:

英文原文:The analysis must be able to determine whether a lock is held, or not held, at every program point. Thus, sections of code where a lock might be held will generate spurious warnings (false positives).

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_THREAD_SAFETY_ANALYSIS 标记来对待。这样的原因在于:初始化期间,仅有一个线程会访问对象,即负责初始化对象的线程。在未获取锁的前提下,初始化需要加锁的成员,这却是安全(常见的代码实践)的。析构函数也是一样的道理。

理想情况下,在对象的初始化操作或销毁操作内部,分析器允许有需要加锁成员的初始化操作,同时其他场景下的通常访问限制依然有效。然而,这在实际的代码实践中确实难以实施的,因为在复杂的指针相关的数据结构中,很难去判断哪个数据是闭合的对象所拥有的。

去掉内联

线程安全分析是严格 intra-procedural 的,就像普通的类型检查。 It relies only on the declared attributes of a function, and will not attempt to inline any method calls. 因此,类似下边的代码可能无法工作:

template<class T>
class AutoCleanup {
  T* object;
  void (T::*mp)();

public:
  AutoCleanup(T* obj, void (T::*imp)()) : object(obj), mp(imp) { }
  ~AutoCleanup() { (object->*mp)(); }
};

Mutex mu;
void foo() {
  mu.Lock();
  AutoCleanup<Mutex>(&mu, &Mutex::Unlock);
  // ...
}  // Warning, mu is not unlocked.

在这个场景下,Autocleanup 的析构函数会调用 mu.Unlock(),所以警告是不正确的。然而,线程安全分析器无法看到这个解锁操作,因为它并不会尝试对析构函数进行内联展开。并且,没有一种方式可以对析构函数添加标注,因为析构函数调用的是一个静态未知的函数。这种模式简单地不做支持即可。

去掉别名的分析

该分析当前并不会跟踪指针别名。然而,如果两个指针都指向同一个互斥锁的时候,就可能会导致误报。

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.mumutex 相等。SCOPED_CAPABILITY 属性处理 MutexLocker 的别名,但仅对特定模式有效。

ACQUIRED_BEFORE(…) 和 ACQUIRED_AFTER(…) 目前并未实现

在以后的更新中会修复。

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

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


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

public:
  MutexLocker(Mutex *mu) ACQUIRE(mu) : mut(mu) {
    mu->Lock();
  }
  ~MutexLocker() RELEASE() {
    mut->Unlock();
  }
};


#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