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