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