エピソード 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);
}
- Chrome のスレッド化とタスク
- Clang スレッド安全性分析: より複雑なシナリオの場合は、他の Clang アノテーションを確認します。