Como o std :: atomic_ref é implementado para tipos não atômicos?

8

Gostaria de saber como pode std::atomic_refser implementado eficientemente (um std::mutexpor objeto) para objetos não atômicos, pois a propriedade a seguir parece bastante difícil de aplicar:

As operações atômicas aplicadas a um objeto por meio de um atomic_ref são atômicas em relação às operações atômicas aplicadas por qualquer outro atomic_ref que faz referência ao mesmo objeto.

Em particular, o seguinte código:

void set(std::vector<Big> &objs, size_t i, const Big &val) {
    std::atomic_ref RefI{objs[i]};
    RefI.store(val);
}

Parece bastante difícil de implementar, pois std::atomic_refseria necessário escolher sempre o mesmo std::mutex(a menos que seja um grande bloqueio mestre compartilhado por todos os objetos do mesmo tipo).

Estou esquecendo de algo? Ou cada objeto é responsável por implementar std::atomic_refe, portanto, é atômico ou carrega um std::mutex?

Nonyme
fonte
2
Eles provavelmente têm um mapa de endereços e mutexs e a aparência do mutex relacionado ao endereço dos objetos. Isso permite várias referências diferentes para proteger um único objeto.
NathanOliver 11/10/19

Respostas:

5

A implementação é muito bonito exatamente o mesmo que std::atomic<T>si. Este não é um problema novo.

Consulte Onde está o bloqueio para um std :: atomic? Uma implementação típica de std::atomic/ std::atomic_refuma tabela hash estática de bloqueios, indexada por endereço, para objetos que não são livres de bloqueio. As colisões de hash levam apenas a contenção extra, não a um problema de correção. (Os impasses ainda são impossíveis; os bloqueios são usados ​​apenas por funções atômicas que nunca tentam levar 2 de cada vez.)

No GCC, por exemplo, std::atomic_refé apenas outra maneira de chamar __atomic_storeum objeto. (Veja o manual do GCC: componentes atômicos )

O compilador sabe se Té pequeno o suficiente para ser livre de bloqueios ou não. Caso contrário, ele chama a função de biblioteca libatomic que usará o bloqueio.

(fato engraçado: isso significa que só funciona se o objeto tiver alinhamento suficiente para atomic<T>. Mas em muitas plataformas de 32 bits, incluindo x86, uint64_tpode ter apenas alinhamento de 4 bytes. atomic_refnesse objeto, ele será compilado e executado, mas não será atômico se o compilador usa um carregamento / armazenamento SSE de 8 bytes no modo de 32 bits para implementá-lo. Felizmente, não há perigo para objetos que tenham alignof(T) == sizeof(T), como a maioria dos tipos primitivos nas arquiteturas de 64 bits.)

Peter Cordes
fonte
Eu não tinha percebido que atomic<T>era permitido também em tipos não atômicos (embora esse possa tecnicamente se alocar a mutexsi próprio, pois é o proprietário do objeto). Obrigado pela explicação, faz sentido.
Nonyme 12/10/19
6

Uma implementação pode usar um hash com base no endereço do objeto para determinar qual conjunto de bloqueios adquirir ao executar a operação.

David Schwartz
fonte
É assim que o libstdc ++ implementa operações atômicas nos shared_ptrobjetos. github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/...
Brian
Essa tabela de hash conteria mutexes? Quando eles são seguros para libertar?
Nonyme 11/10/19
@Nonyme No link que forneci, você pode ver que eles têm duração de armazenamento estático.
Brian