Zircon 代码利用 Clang 的线程安全性分析功能 记录和机器验证一些同步不变量。这些 在针对 Clang 进行构建时,会检查注解(请参阅 使用入门 clang)。
使用方法
在 Zircon 中,我们提供自己的一组宏,用于封装注释,
为我们的同步基元添加了注解。在编写涉及
同步或为现有代码添加注释,在大多数情况下,您应该使用
由 Cloud Build 提供的线程注解宏
<lib/zircon-internal/thread_annotations.h.
这些宏都以前缀 "TA_"
开头,用于线程分析。最
常用选项包括:
TA_GUARDED(x)
:带注解的变量受 capability(例如锁)保护x
TA_ACQ(x...)
函数获取x
集中的所有互斥量并在返回后保留它们TA_REL(x...)
函数释放x
集合中的所有互斥量TA_REQ(x...)
函数要求调用方保留x
集中的所有互斥量。TA_EXCL(x...)
函数要求调用方不得持有x
组中的任何互斥量
例如,包含成员变量'int foo_'
的类受
互斥量的注解将如下所示:
// example.h
class Example {
public:
// Public function has no locking requirements and thus needs no annotation.
int IncreaseFoo(int by);
private:
// This is an internal helper routine that can only be called with |lock_|
// held. Calling this without holding |lock_| is a compile-time error.
// Annotations like TA_REQ, TA_ACQ, TA_REL, etc are part of the function's
// interface and must be on the function declaration, usually in the header,
// not the definition.
int IncreaseFooLocked(int by) TA_REQ(lock_);
// This internal routine requires that both |lock_| and |foo_lock_| be held by the
// caller.
int IncreaseFooAndBarLocked(int foo_by, int bar_by) TA_REQ(lock_) TA_REQ(bar_lock_);
// The TA_GUARDED(lock_) annotation on |foo_| means that |lock_| must be
// held to read or write from |foo_|.
int foo_ TA_GUARDED(lock_);
// |lock_| can be declared after annotations referencing it,
// if desired.
Mutex lock_;
Mutex bar_lock_;
};
// example.cpp
int Example::IncreaseFoo(int by) {
int new_value;
{
AutoLock lock(&lock_); // fbl::AutoLock is annotated
new_value = IncreaseFooLocked(by);
}
return new_value;
}
请注意,对于允许成组互斥对象的注释, 多次应用注释,也可以为 注解。换句话说,以下两个声明是等效的。
int IncreaseFooAndBarLocked(int foo_by, int bar_by) TA_REQ(lock_) TA_REQ(bar_lock_);
int IncreaseFooAndBarLocked(int foo_by, int bar_by) TA_REQ(lock_, bar_lock_);
通过 sysroot 公开的库代码必须使用更糟糕的名称 宏的 system/public/zircon/compiler.h 更改为 避免与 sysroot 的使用者发生冲突。
最佳做法
注解应该对注解和标识符进行补充, 易于理解。注释不会取代注释或清除名称。请尝试 在编写涉及锁定的代码时,请遵循以下最佳实践:
受锁和锁保护的群组成员变量。影响因素 除了 注释。例如,多个成员变量受一个锁保护时 和多个注释都由一种不同的锁保护,因此注释比 会逐一讲解每个注释
命名需要锁的函数使用“Locked()”存储后缀。如果有 为调用函数而可能持有的多个锁,请考虑 并在函数名称中明确说明选择谨记致电者 代码将无法看到注释。
限制
线程安全分析是在编译时执行的纯静态检查,
无法理解有条件持有的锁,或跨越多个
编译单元。在许多
此分析仍然有用,但也存在一些
根本无法理解。停用分析的主要应急路径
向函数定义中添加注解 TA_NO_THREAD_SAFETY_ANALYSIS
包含容易导致分析混淆的代码。其他转义机制包括
- 请参阅 Clang 文档了解详情。导致
因为这对人类理解可能很复杂,所以需要停用分析
而且应该附带注释,指示
使用中的不变性。
线程安全分析可能在很多方面失效,例如,
使用指针时的注意事项例如,在获取受保护数据的地址时,
成员 Clang 无法跟踪 Guard,例如适用于受锁保护的 foo_
在没有持有锁的情况下对 memset(&foo_, 0, sizeof(foo_))
的调用不会被捕获
视为违规行为。