Chronicle Chromium nr 25: adnotacje dotyczące bezpieczeństwa wątków

Odcinek 25: Victor Costan w SFO (październik 2021 r.)
Poprzednie odcinki

W C++ możliwość prowadzenia wyścigów danych sprowadza się do niewielkiego dowodu prawidłowości w zakresie bezpieczeństwa wątków w przypadku każdego dostępu do danych. Te dowody są bardzo pracochłonne, zwłaszcza podczas przeglądania lub refaktoryzacji kodu. Statyczna platforma analizy Clang przejmuje całą ciężką pracę zabezpieczeń wątków.

Dodaj GUARDED_BY_CONTEXT() do uczestników danych w klasach niezabezpieczonych wątkami

Większość klas Chrome jest niebezpieczna dla wątków i powinna być używana w jednej sekwencji. Dodaj adnotacje do wszystkich elementów danych, które nie są bezpieczne w wątkach. Zbędne adnotacje są bezpieczne, natomiast brak adnotacji zwiększa ryzyko ich generowania.

#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 wymusza sprawdzanie sekwencji

W zamian za dodawanie adnotacji do użytkowników Clang dba o to, aby każda metoda, która uzyskuje dostęp do danych, przeprowadziła kontrolę bezpieczeństwa sekwencji, zanim to zrobi. W trakcie refaktoryzacji kodu Clang kontynuuje egzekwowanie adnotacji 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);
}

Dodawanie GUARDED_BY() do uczestników danych w klasach bezpiecznych dla wątków, które korzystają z muteksów

Niektóre zajęcia w Chrome muszą używać blokad ze względu na bezpieczeństwo wątków. W takich przypadkach dodaj adnotacje do wszystkich danych, które nie są bezpieczne w wątku. Każda adnotacja wskazuje muteks, który musi być zablokowany podczas uzyskiwania dostępu do użytkownika danych.

#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 wymusza pozyskanie blokady

Zaczekaj i pozwól kompilatorowi sprawdzić, czy każdy element base::AutoLock ma prawidłowy zakres i że wywołania blokady Acquire() i Release() są prawidłowo sparowane.

void ThreadSafeCache::Set(base::StringPiece key, base::StringPiece value) {
  base::AutoLock auto_lock(lock_);  // Clang warns without this.
  data_.emplace(key, value);
}