Chromium Chronicle Nr. 25: Sicherheitshinweise für Threads

Folge 25: von Victor Costan in SFO (Oktober 2021)
Vorherige Folgen

In C++ geht es nur um einen kleinen Richtigkeitsnachweis für die Threadsicherheit bei jedem Datenzugriff. Diese Beweise führen zu einer Menge mentalen Aufwand, insbesondere bei der Überprüfung oder Refaktorierung von Code. Das statische Analyse-Framework von Clang übernimmt den Aufwand für die Thread-Sicherheit.

GUARDED_BY_CONTEXT() zu Datenmitgliedern in Thread-Unsichere Klassen hinzufügen

Die meisten Chrome-Klassen sind threadunsicher und sollten für eine einzelne Sequenz verwendet werden. Fügen Sie allen Datenmitgliedern, die nicht threadsicher sind, Anmerkungen hinzu. Unnötige Annotationen sind sicher, fehlende Annotationen stellen jedoch das Risiko von Datenrennen dar.

#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 erzwingt Sequenzprüfungen

Als Gegenleistung werden die Datenmitglieder Clang sorgt dafür, dass jede Methode, die auf die Daten zugreift, vorher eine Sequenzsicherheitsprüfung durchführt. Wenn Code in Refaktorierungen verschoben wird, Clang erzwingt weiterhin die Annotation 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() zu Datenmitgliedern in threadsicheren Klassen hinzufügen, die Mutexe verwenden

Einige Klassen in Chrome müssen für Threadsicherheit Sperren verwenden. Annotieren Sie in diesen Fällen alle Datenmitglieder, die nicht threadsicher sind. Jede Annotation verweist auf einen Mutex, der während des Zugriffs auf das Datenmitglied bestehen muss.

#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 erzwingt Akquisitionssperren

Halten Sie den Vorgang zurück und lassen Sie den Compiler überprüfen, ob jeder base::AutoLock korrekt zugeordnet ist. und die Sperraufrufe Acquire() und Release() sind korrekt gekoppelt.

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