Chromium Chronicle n.o 25: Anotaciones de seguridad de los subprocesos

Episodio 25: de Victor Costan en SFO (octubre de 2021)
Episodios anteriores

En C++, descartar la posibilidad de carreras de datos se reduce a una pequeña prueba de precisión de seguridad de subprocesos para cada acceso de miembro a los datos. Estas pruebas suman mucho trabajo mental, especialmente cuando se revisa o refactoriza el código. El framework de análisis estático de Clang se hace cargo del trabajo repetitivo de las pruebas de seguridad de subprocesos.

Se agregó GUARDED_BY_CONTEXT() a los miembros de datos de las clases no seguras en los subprocesos.

La mayoría de las clases de Chrome no son seguras para los subprocesos y deben usarse en una sola secuencia. Agrega anotaciones a todos los miembros de datos que no sean seguros para los subprocesos. Las anotaciones innecesarias son seguras, pero la ausencia de anotaciones es un riesgo de carreras de datos.

#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 aplica verificaciones de secuencias

A cambio de anotar los miembros de datos, Clang garantiza que cualquier método que acceda a los datos realice una verificación de seguridad de la secuencia antes de hacerlo. A medida que el código se mueve en las refactorizaciones, Clang continúa aplicando la anotación 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);
}

Agrega GUARDED_BY() a los miembros de datos en clases con seguridad en subprocesos que usan exclusiones mutuas.

Algunas clases de Chrome deben usar bloqueos para garantizar la seguridad de los subprocesos. En estos casos, anota todos los miembros de datos que no sean seguros para los subprocesos. Cada anotación apunta a una exclusión mutua que debe conservarse mientras se accede al miembro de datos.

#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 aplica de manera forzosa las adquisiciones de bloqueo

Espera un momento y permite que el compilador se asegure de que cada base::AutoLock tenga el alcance correcto y que las llamadas de bloqueo Acquire() y Release() se vinculen correctamente.

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