Wie wird std :: atomic_ref für nichtatomare Typen implementiert?

8

Ich frage mich, wie std::atomic_refeffizient (eine std::mutexpro Objekt) für nichtatomare Objekte implementiert werden kann , da die folgende Eigenschaft ziemlich schwer durchzusetzen scheint:

Atomoperationen, die über eine atomare Referenz auf ein Objekt angewendet werden, sind atomar in Bezug auf atomare Operationen, die über eine andere atomare Referenz angewendet werden, die auf dasselbe Objekt verweist.

Insbesondere der folgende Code:

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

Scheint ziemlich schwierig zu implementieren, da das std::atomic_refirgendwie jedes Mal das gleiche auswählen müsste std::mutex(es sei denn, es ist eine große Master-Sperre, die von allen Objekten des gleichen Typs gemeinsam genutzt wird).

Vermisse ich etwas Oder ist jedes Objekt für die Implementierung verantwortlich std::atomic_refund daher entweder atomar oder trägt ein std::mutex?

Nonyme
quelle
2
Sie haben wahrscheinlich eine Karte mit Adressen und Mutexen und das Aussehen des Mutex, der sich auf die Objektadresse bezieht. Dadurch können mehrere verschiedene Referenzen ein einzelnes Objekt schützen.
NathanOliver

Antworten:

5

Die Implementierung ist ziemlich genau die gleiche wie die std::atomic<T>selbst. Dies ist kein neues Problem.

Siehe Wo ist die Sperre für ein std :: atomic? Eine typische Implementierung std::atomic/ std::atomic_refeine statische Hash - Tabelle von Sperren, durch Adresse indiziert, für nicht-lock freier Objekte. Hash-Kollisionen führen nur zu zusätzlichen Konflikten, nicht zu einem Korrektheitsproblem. (Deadlocks sind immer noch unmöglich; die Locks werden nur von atomaren Funktionen verwendet, die niemals versuchen, 2 gleichzeitig zu nehmen.)

In GCC ist dies beispielsweise std::atomic_refnur eine weitere Möglichkeit, __atomic_storeein Objekt aufzurufen . (Siehe das GCC-Handbuch: Atomic Builtins )

Der Compiler weiß, ob er Tklein genug ist, um sperrenfrei zu sein oder nicht. Wenn nicht, ruft es die libatomische Bibliotheksfunktion auf, die die Sperre verwendet.

(Unterhaltsame Tatsache: Das bedeutet, dass es nur funktioniert, wenn das Objekt ausreichend ausgerichtet ist atomic<T>. Auf vielen 32-Bit-Plattformen, einschließlich x86, ist uint64_tmöglicherweise nur eine 4-Byte-Ausrichtung vorhanden. atomic_refAuf einem solchen Objekt wird es kompiliert und ausgeführt, ist jedoch nicht atomar, wenn Der Compiler verwendet ein SSE-8-Byte-Laden / Speichern im 32-Bit-Modus, um es zu implementieren. Glücklicherweise besteht keine Gefahr für Objekte, die alignof(T) == sizeof(T)wie die meisten primitiven Typen auf 64-Bit-Architekturen vorhanden sind.)

Peter Cordes
quelle
Ich hatte nicht bemerkt, atomic<T>dass dies auch für nichtatomare Typen erlaubt war (obwohl man sich technisch ein zuordnen mutexkonnte, da es das Objekt besitzt). Danke für die Erklärung, es macht Sinn.
Nonyme
6

Eine Implementierung kann einen Hash basierend auf der Adresse des Objekts verwenden, um zu bestimmen, welche einer Reihe von Sperren während der Ausführung der Operation erfasst werden sollen.

David Schwartz
quelle
Auf diese Weise implementiert libstdc ++ atomare Operationen an shared_ptrObjekten. github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/…
Brian
Diese Hash-Tabelle würde Mutexe enthalten? Wann sind sie sicher zu befreien?
Nonyme
@Nonyme In dem von mir bereitgestellten Link können Sie sehen, dass sie eine statische Speicherdauer haben.
Brian