Chromium Chronicle #25: anotações de segurança de linhas de execução

Episódio 25:de Victor Costan em São Francisco (outubro de 2021)
Episódios anteriores

Em C++, excluir a possibilidade de disputa de dados se resume a uma pequena prova de precisão de segurança da linha de execução para cada acesso de membro dos dados. Essas provas geram muito trabalho mental, especialmente ao revisar ou refatorar o código. O framework de análise estática do Clang assume o trabalho de provas de segurança de linhas de execução.

Adicionar GUARDED_BY_CONTEXT() a membros de dados em classes não seguras para linhas de execução

A maioria das classes do Chrome não é segura para as linhas de execução e precisa ser usada em uma única sequência. Adiciona anotações a todos os membros de dados que não são thread-safe. Anotações desnecessárias são seguras, mas anotações ausentes podem causar disputa de dados.

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

O Clang aplica verificações de sequência

Em troca de anotação dos membros de dados, o Clang garante que qualquer método que acesse os dados execute uma verificação de segurança de sequência antes disso. À medida que o código é movido nas refatorações, o Clang continua aplicando a anotação 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);
}

Adição de GUARDED_BY() a membros de dados em classes seguras para linhas de execução que usam mutexes.

Algumas classes no Chrome precisam usar bloqueios para segurança da linha de execução. Nesses casos, anote todos os membros de dados que não são thread-safe. Cada anotação aponta para um mutex que precisa ser mantido ao acessar o membro de dados.

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

O Clang aplica aquisições de bloqueio

Volte e deixe o compilador garantir que cada base::AutoLock tenha o escopo correto e que as chamadas Acquire() e Release() de bloqueio sejam pareadas corretamente.

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