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