¿Cómo se implementa std :: atomic_ref para los tipos no atómicos?

8

Me pregunto cómo se std::atomic_refpuede implementar de manera eficiente (uno std::mutexpor objeto) para objetos no atómicos, ya que la siguiente propiedad parece bastante difícil de aplicar:

Las operaciones atómicas aplicadas a un objeto a través de atomic_ref son atómicas con respecto a las operaciones atómicas aplicadas a través de cualquier otra atomic_ref que haga referencia al mismo objeto.

En particular, el siguiente 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, ya std::atomic_refque de alguna manera necesitaría elegir cada vez lo mismo std::mutex(a menos que sea un gran bloqueo maestro compartido por todos los objetos del mismo tipo).

¿Me estoy perdiendo de algo? ¿O cada objeto es responsable de implementar std::atomic_refy, por lo tanto, ser atómico o llevar un std::mutex?

Nonyme
fuente
2
Probablemente tengan un mapa de direcciones y mutexs y el aspecto del mutex que se relaciona con la dirección de los objetos. Esto permite múltiples referencias diferentes para proteger un solo objeto.
NathanOliver

Respuestas:

5

La implementación es casi exactamente la misma que std::atomic<T>ella misma. Este no es un problema nuevo.

Vea ¿Dónde está el bloqueo para un std :: atomic? Una aplicación típica de std::atomic/ std::atomic_refuna tabla hash estático de cerraduras, indexada por dirección, para objetos no-libre de bloqueo. Las colisiones de hash solo conducen a una contención adicional, no a un problema de corrección. (Los bloqueos siguen siendo imposibles; los bloqueos solo son utilizados por funciones atómicas que nunca intentan tomar 2 a la vez).

En GCC, por ejemplo, std::atomic_refes solo otra forma de invocar __atomic_storeun objeto. (Consulte el manual de GCC: incorporaciones atómicas )

El compilador sabe si Tes lo suficientemente pequeño como para ser libre de bloqueo o no. Si no, llama a la función de biblioteca libatomic que usará el bloqueo.

(Dato curioso: eso significa que solo funciona si el objeto tiene suficiente alineación atomic<T>. Pero en muchas plataformas de 32 bits, incluida x86, uint64_tpuede que solo tenga una alineación de 4 bytes. atomic_refEn dicho objeto se compilará y ejecutará, pero en realidad no será atómico si el compilador utiliza una carga / almacenamiento SSE de 8 bytes en modo de 32 bits para implementarlo. Afortunadamente no hay peligro para los objetos que tienen alignof(T) == sizeof(T), como la mayoría de los tipos primitivos en arquitecturas de 64 bits).

Peter Cordes
fuente
No me había dado cuenta de que también atomic<T>estaba permitido en tipos no atómicos (aunque técnicamente podría asignarse uno mutex, ya que posee el objeto). Gracias por la explicación, tiene sentido.
Nonyme
6

Una implementación puede usar un hash basado en la dirección del objeto para determinar cuál de un conjunto de bloqueos adquirir mientras se realiza la operación.

David Schwartz
fuente
Así es como libstdc ++ implementa operaciones atómicas en shared_ptrobjetos. github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/…
Brian
¿Dicha tabla hash contendría mutexes? ¿Cuándo son seguros para liberar?
Nonyme
@Nonyme En el enlace que proporcioné, puede ver que tienen una duración de almacenamiento estático.
Brian