Thread Safety Analysis

参考:Clang 15.0.0git documentation THREAD SAFETY ANALYSIS

1. 简介

Clang线程安全分析是一种C++扩展,代码中存在潜在的竞争时会发出警告。线程安全分析是完全静态的(即编译时);没有运行时开销。该线程安全分析仍在积极开发中,但已经足够成熟,可以在工业环境中部署。它由谷歌与CERT/SEI合作开发,广泛用于谷歌的内部代码库。

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

2. 开始
#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属性声明线程必须先锁定mu,然后才能读写balance,从而确保递增和递减操作是原子的。类似地,REQUIRES声明调用线程必须在调用withdrawImpl之前锁定mu。由于假定调用方已锁定mu,因此可以安全地修改方法中的balance 。

depositImpl()方法没有REQUIRES,因此分析会发出警告。线程安全分析不是过程间的,因此必须明确声明调用方需求。transferFrom()中还有一个警告,因为尽管该方法锁定了this->mu,但它不会锁定b.mu。分析表明,这是两个不同对象中的两个独立mutex。

最后,withdraw()方法中还有一个警告,因为它无法解锁mu。每个锁都必须有相应的解锁,分析将检测双重锁定和双重解锁。函数可以在不释放锁的情况下获取锁(反之亦然),但必须对其进行注释(使用ACQUIRE/RELEASE)。

3. 运行分析

要运行分析,只需使用-Wthread-safety flag,例如

clang -c -Wthread-safety example.cpp

请注意,本例假设存在一个经过适当注释的mutex声明了哪些方法执行锁定、解锁等操作。

4. 基本概念:Capabilities

线程安全分析提供了一种利用功能(Capabilities)保护资源的方法。资源要么是数据成员,要么是提供对某个底层资源的访问的函数/方法。分析确保调用线程无法访问资源(即调用函数或读/写数据),除非它有能力(capability)这样做。

能力(Capabilities)与命名C++对象相关联,这些对象声明特定方法来获取和释放capability。对象的名称用于标识capability。最常见的例子是mutex。例如,如果mu是mutex,则调用mu.Lock()使调用线程获得访问受mu保护的数据的capability。同样,调用mu.Unlock()释放了这种capability。

线程可以独占或共享某个功能(capability)。独占功能一次只能由一个线程持有,而共享功能可以由多个线程同时持有。这种机制实施了多读单写模式。对受保护数据的写入操作需要独占访问,而读取操作只需要共享访问。

5. 参考指南

线程安全性分析使用属性(attributes)来声明线程约束。属性必须附加到命名声明,例如类、方法和数据成员。强烈建议用户为各种属性定义宏;示例定义可以参考下面mutex.h。

这些属性仅控制线程安全分析所做的假设及其发出的警告。它们不会影响运行时生成的代码或行为。

6. GUARDED_BY和PT_GUARDED_BY

GUARDED_BY是数据成员的一个属性(attribute ),它声明数据成员受给定功能(capability)的保护。对数据的读操作需要共享访问,而写操作需要独占访问。

PT_GUARDED_BY与之类似,但用于指针和智能指针。数据成员本身没有约束,但它指向的数据受给定功能(capability)的保护。

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.
}
7. REQUIRES(…), REQUIRES_SHARED(…)

Previously: EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED

REQUIRES是函数或方法的一个属性(attribute),它声明调用线程必须以独占方式访问给定的功能(capabilities)。
可以指定多个功能。这些功能必须在进入功能时保持,在退出功能时仍然保持。

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();
}
8. ACQUIRE(…), ACQUIRE_SHARED(…), RELEASE(…), RELEASE_SHARED(…), RELEASE_GENERIC(…)

Previously: EXCLUSIVE_LOCK_FUNCTION, SHARED_LOCK_FUNCTION, UNLOCK_FUNCTION

ACQUIRE和ACQUIRE_SHARED是函数或方法上的属性(attributes ),声明函数获取了功能(capability),但没有释放它。
给定的能力不能在进入时保持,而将在退出时保持(独占用ACQUIRE,共享用ACQUIRE_SHARED)。

RELEASE、RELEASE_SHARED和RELEASE_GENERIC声明函数释放给定的功能(capability)。该能力必须在进入时保持(独占用RELEASE、共享用RELEASE_SHARED、独占或共享用RELEASE_GENERIC),并且在退出时不再保持。

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,并且分析不会检查函数体。
此模式旨在供将锁定细节隐藏在抽象接口后面的类使用。例如:

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();
}
9. EXCLUDES(…)

Previously: LOCKS_EXCLUDED

EXCLUDES是函数或方法的一个属性(attribute),它声明调用方不能持有给定的功能(capabilities)。此注释用于防止死锁。许多mutex实现都不是可重入的,因此如果函数第二次获取mutex,可能会发生死锁。

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是可选的。如果属性(attribute)缺失,分析不会发出警告,这在某些情况下可能会导致误判。这个问题将在Negative Capabilities中进一步讨论。

10. NO_THREAD_SAFETY_ANALYSIS

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

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

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

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

11. RETURN_CAPABILITY

Previously: LOCK_RETURNED
RETURN_CAPABILITY是函数或方法的一个属性(attribute),它声明函数返回对给定功能的引用。它用于注释返回mutex的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; }
};
12. ACQUIRED_BEFORE(…), ACQUIRED_AFTER(…)

ACQUIRED_BEFORE和ACQUIRED_AFTER是成员声明的属性(attributes),特别是mutexes或其他功能(capabilities)的声明。这些声明强制必须以特定顺序获取mutexes,以防止死锁。

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

Previously: LOCKABLE
CAPABILITY是类的一个属性(attribute),它指定类的对象可以被当做一个功能(capability)。字符串参数指定错误消息中的功能类型,例如mutex。请参见上面给出的示例,或mutex.h中的Mutex类。

14. SCOPED_CAPABILITY

Previously: SCOPED_LOCKABLE
SCOPED_CAPABILITY是实现RAII风格锁定的类上的一个属性(attribute),在该类中,一个功能(capability)在构造函数中获得,并在析构函数中释放。此类类需要特殊处理,因为构造函数和析构函数通过不同的名称引用功能;请参见下面mutex.h中的MutexLocker类。

作用域能力(capabilities)被视为在构造时隐式获取并在销毁时释放的能力。 它们与构造函数的线程安全属性中命名的一组(常规)功能相关联。
其他成员函数上的Acquire-type属性被视为应用于该组关联功能,而RELEASE意味着函数以任何模式释放所有关联功能。

15. TRY_ACQUIRE(, …), TRY_ACQUIRE_SHARED(, …)

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.
  }
}
16. ASSERT_CAPABILITY(…) and ASSERT_SHARED_CAPABILITY(…)

Previously: ASSERT_EXCLUSIVE_LOCK, ASSERT_SHARED_LOCK

这些是一个函数或方法的属性,该函数或方法断言调用线程已经拥有给定的功能,例如通过执行运行时测试并在未拥有能力(capability)时终止。此注释的存在会让线程安全分析(Thread Safety Analysis)假设在调用带注释的函数后保持该功能。参见下面的 mutex.h。

17. GUARDED_VAR and PT_GUARDED_VAR

不推荐使用这些属性。

18. Warning flags

-Wthread-safety
-Wthread-safety-attributes: 语义检查线程安全属性
-Wthread-safety-analysis:核心分析
-Wthread-safety-precise:要求mutex表达式精确匹配
对于有很多别名的代码,可以禁用此警告。
-Wthread-safety-reference:检查通过引用传递的受保护成员时

Negative Capabilities 是一项实验性功能,可通过以下方式启用:
-Wthread-safety-negative:-Wthread-safety-negative:负面能力,默认关闭

当新功能和检查添加到分析中时,它们通常会引入额外的警告。这些警告最初作为测试版警告发布一段时间,之后将迁移到标准分析中。

-Wthread-safety-beta:-Wthread安全测试版:新功能,默认情况下关闭

19. Negative Capabilities

线程安全分析旨在防止竞争条件和死锁。 GUARDED_BY 和 REQUIRES 属性通过确保在读取或写入受保护的数据之前保持能力(capability)来防止竞争条件,而 EXCLUDES 属性通过确保不保持mutex来防止死锁。

但是,EXCLUDES 是一个可选属性,不提供与 REQUIRES 相同的安全保证。 特别是:
获得某种能力的函数不必排除它。
调用排除某个功能的函数的函数没有传递性地排除该功能。

结果,EXCLUDES 很容易产生误报:

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

负面要求是一种提供更强安全保障的替代方案。负面需求使用REQUIRES属性,与!操作符结合,表示不应保持某一能力。
例如,使用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);
};

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

20. Frequently Asked Questions

Q.我应该将属性放在头文件中,还是放在 .cc/.cpp/.cxx 文件中?
A.属性是函数正式接口的一部分,应该始终放在头文件中,引入头文件的任何地方都可以看到它们。.cpp文件中的属性在直接翻译单元之外不可见,这会导致误报。
Q.“互斥锁不是锁在通过这里的每条路径上吗?” 那是什么意思?
A.参见下文No conditionally held locks.

21. Known Limitations

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

class Foo;

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

class Foo {
  Mutex mu;
};
22. 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 方法,该方法仅用于线程安全分析。

23. 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();
}
24. No checking inside constructors and destructors

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

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

25. No inlining

线程安全性分析是严格的过程内分析,就像普通类型检查一样。它仅依赖于函数的声明属性,而不依赖于函数的任何内联属性。因此,以下代码将不起作用:

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(),因此警告是虚假的。 但是,线程安全分析看不到解锁,因为它不会尝试内联析构函数。 此外,没有办法对析构函数进行注释,因为析构函数正在调用一个静态未知的函数。 根本不支持这种模式。

26. No alias analysis

该分析目前不跟踪指针别名。因此,如果两个指针都指向同一个互斥锁,可能会出现误报。

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
}

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

27. ACQUIRED_BEFORE(…) and ACQUIRED_AFTER(…) are currently unimplemented.

将在将来的更新中修复。

28. 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) {}

  // 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
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值