2013-08-16 5 views
9

Ich möchte, dies zu tun:Können zwei std :: atomics Teil einer Union sein?

union { 
    std::atomic<uint128_t> u128; 
    struct { 
     std::atomic<uint64_t> u64_1; 
     std::atomic<uint64_t> u64_2; 
    }; 
}; 

Mehrere Threads gelesen und beide Teile der Union schreiben.

Ist es sicher?

Edit: Ich benutze Linux, x86_64, klirren 3.3

Edit2: Ich möchte in der Lage sein, zu erhöhen und verringern u64_1, lesen u64_2 und schreiben U128 (compare_exchange)

Edit3 : Was ist, wenn ich atomic builtin functions verwende? Die Gewerkschaft wird wie folgt aussehen:

union { 
    uint128_t u128; 
    struct { 
     uint64_t u64_1; 
     uint64_t u64_2; 
    }; 
}; 

u64_1 zur ersten Hälfte des U128 abbildet und u64_2 wird auf die zweite Hälfte der Karte.

+0

Unterstützt Ihre Plattform 128bit atomar schreibt nativ? –

+1

Aufgrund von Aliasing-Einschränkungen (mindestens) können Sie nicht in u128 schreiben und von u64_1 und u64_2 lesen oder umgekehrt. Auch C++, auch C++ 11, hat keine anonymen Strukturen. – bames53

+2

Sieht aus wie ein BFG-3000 mit einem eingebauten Fußdetektor für mich ... – twalberg

Antwort

7

Die std::atomic<T> Operationen können entweder sperren oder sperren, je nachdem, ob die Architektur die zugrunde liegenden Garantien bietet. Sie können dies überprüfen, indem Sie std::atomic<T>::is_lock_free() überprüfen.

Wenn der Typ nicht sperrfrei ist, wird er möglicherweise von der Bibliothek mit einem Zähler unterstützt. Das wiederum bedeutet wahrscheinlich, dass der Typ nicht länger ein POD darunter ist, was wiederum bedeutet, dass es in Ihrer Verantwortung liegt, die Konstruktoren/Destruktoren aufzurufen, wenn Sie von einem aktiven Mitglied der Union zu einem anderen wechseln. Wenn es einen Mutex für die 128-Bit-, nicht aber die 64-Bit-Typen gibt, könnte es passieren, dass das Layout der Werte übereinstimmt, aber die Atomizität der Operationen wird durch verschiedene Mittel garantiert, so scheint es arbeiten, aber scheitern falsch und in einer Weise, die schwer zu erkennen ist.

+2

§ 29.5/5 sagt "Die atomaren Integralspezialisierungen und die Spezialisierung" atomic 'müssen Standard-Layout haben. Sie haben jeweils einen trivialen Standardkonstruktor und einen trivialen Destruktor. Sie unterstützen jeweils Aggregat Initialisierungssyntax. " Daher sind keine Konstruktor-/Destruktoraufrufe erforderlich, um das aktive Union-Member zu wechseln. – Casey

+1

@Casey: Ja, ich habe das gelesen, bevor ich die Antwort geschrieben habe. Die Frage ist, ob 'uint128_t' ein * ganzzahliger Typ * gemäß dem Standard ist. Der wichtige Punkt ist, dass, wenn der Typ nicht nativ unterstützt wird, ein zugrunde liegender Synchronisationsmechanismus verwendet werden muss, und es gibt keine Garantie, dass der Synchronisationsmechanismus das Standardlayout ist oder nicht. –

+1

@Casey: Beachten Sie auch, dass * Standard-Layout * und * POD * nicht äquivalent sind. Alle PODs sind Standard-Layout, aber nicht alle Standard-Layout-Typen sind POD. Lesen Sie den zweiten Absatz als: Wenn die 'std :: atomic ' keinen * trivialen Konstruktor * hat ... –

0

Es sollte in Ordnung sein, solange

1 Sie strenge Aliasing zu respektieren;

2 Sie nicht aktualisieren u128 und entweder von u64_1 oder u64_2 gleichzeitig (was natürlich ist hier nicht geschützt und macht keinen Sinn sowieso);

3 Sie nicht erwarten, u64_1 und u64_1 der ersten und zweiten 64 Bits von u128 abzubilden.

Ich sehe keinen Vorteil von diesem Konstrukt. Vielleicht können Sie klären, warum dieses Konstrukt hilfreich sein sollte. Ich denke, dass Sie besser ohne die u128 (d. H. Ohne eine Union) sein könnten.

+0

Ich denke, er möchte Threads gleichzeitig versuchen, Änderungen an der 128bit oder der High/Low 64bit (dh gemischte Operationen): schreiben 128, lesen 64, schreiben 64, lesen 128 ... die nicht ganz garantiert –

0

Unter der Annahme, dass alle Mitglieder der Union in der Lage sein wollen, gleichzeitig zu verwenden:

portabel, nein. Dies wird kein definiertes Verhalten haben: Berücksichtigen Sie, dass einer oder beide Typen mit Mutexen implementiert werden können, um das Sperren zu ermöglichen.

Eingeschränkt auf eine bestimmte Implementierung, für die alle beteiligten atomaren Typen über lockfreie Implementierungen verfügen, ist es sehr wahrscheinlich, dass sie korrekt oder zumindest so korrekt funktionieren, wie dies bei einer ähnlichen Union von Nicht-Atomics der Fall wäre.

+0

Nun, C++ 11 erlaubt Nicht-POD-Vereinigungen, solange Sie alle Regeln befolgen und vergessen Sie nicht, den ctor/dtor manuell aufzurufen ... Zu fummelig. –

+0

@MaximYegorushkin Sorry, ich vergesse die Annahme, dass OP möchte alle Mitglieder gleichzeitig ändern, was meine Lesung von "Mehrere Threads lesen und schreiben beide Teile der Union." – Casey

+0

das würde zu undefiniertem Verhalten führen –

4

Im Allgemeinen ist dies nicht sicher.Die Note (admitedly nicht-normativ) in 29,5/9 sagt uns, dass:

[Hinweis: Die Darstellung eines Atom Spezialisierung nicht die gleiche Größe wie sein entsprechendes Argument Typ zu haben braucht. Spezialisierungen sollten nach Möglichkeit die gleiche Größe haben, da dies den Aufwand für die Portierung des vorhandenen Codes verringert. -end note]

So richtig vorne sind wir nicht garantiert, dass die beiden Teile sogar die gleiche Größe haben. Außerdem müssen Sie sicherstellen, dass Sie keine atomare Operation für ein Mitglied verwenden, während ein anderes das aktive Mitglied in einem anderen ändert.

+0

Beide Mitglieder sind die ganze Zeit aktiv – alkedr

+2

@alkedr Der C++ - Standard besagt explizit, dass nur ein Mitglied einer Union gleichzeitig aktiv ist. –

+2

@alkedr: Mark ist korrekt. Es ist ein vollständiger Mythos, dass Gewerkschaften jederzeit mehrere aktive Mitglieder haben können. Viele Leute benutzen Gewerkschaften aus diesem einzigen Grund und sie sind alle falsch. '[C++ 11 9.5/1]: In einer Vereinigung kann höchstens einer der nicht statischen Datenmitglieder zu jeder Zeit aktiv sein, dh der Wert von höchstens einem der nicht statischen Datenelemente kann sein jederzeit in einer Gewerkschaft gespeichert. [..] ' –

Verwandte Themen