Chromium Chronicle #25:线程安全注解

第 25 集:由 Victor Costan 在 SFO 创作(2021 年 10 月)
上一集

在 C++ 中,排除数据争用的可能性取决于针对每个数据成员访问的小型线程安全正确性证明。 这些证明累加了很多费力的劳动,尤其是在审核或重构代码时。 Clang 的静态分析框架接管了线程安全证明的繁琐工作

GUARDED_BY_CONTEXT() 添加到线程不安全类中的数据成员

大多数 Chrome 类都是线程不安全的,应在单个序列中使用。 为所有非线程安全的数据成员添加注解。 不必要的注解是安全的,但缺少注解可能会导致数据争用。

#include "base/sequence_checker.h"  // for SEQUENCE_CHECKER()
#include "base/thread_annotations.h"  // for GUARDED_BY_CONTEXT()

class Cache {
  // Methods here.
 private:
  SEQUENCE_CHECKER(sequence_checker_);
  base::flat_map<std::string, std::string> data_ GUARDED_BY_CONTEXT(sequence_checker_);
};

Clang 强制执行序列检查

作为对数据成员进行注释的回报 Clang 会确保任何访问数据的方法都会先执行序列安全检查。 随着代码在重构中移动, Clang 继续强制执行 GUARDED_BY_CONTEXT() 注解。

void Cache::Set(base::StringPiece key, base::StringPiece value) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);  // Clang warns without this.
  data_.emplace(key, value);
}

GUARDED_BY() 添加到使用互斥量的线程安全类中的数据成员

Chrome 中的某些类必须使用锁来确保线程安全。 在这些情况下,为所有并非线程安全的数据成员添加注解。 每个注解都指向一个互斥量,在访问数据成员时必须持有该互斥量。

#include "base/thread_annotations.h"  // for GUARDED_BY()

class ThreadSafeCache {
  // Methods here.
  private:
    base::Lock lock_;
    base::flat_map<std::string, std::string> data_ GUARDED_BY(lock_);
};

Clang 强制执行锁获取

别着急,让编译器确保每个 base::AutoLock 的作用域都正确无误, 并且锁的 Acquire()Release() 调用已正确配对。

void ThreadSafeCache::Set(base::StringPiece key, base::StringPiece value) {
  base::AutoLock auto_lock(lock_);  // Clang warns without this.
  data_.emplace(key, value);
}