Используя встроенные CCC-примитивы GCC, мы можем выполнить атомную операцию CAS с помощью __atomic_compare_exchange
.
В отличие от типа С++ 11 std::atomic
, атомарные примитивы GCC C работают на регулярных неатомных интегральных типах, включая 128-битные целые числа на платформах, где поддерживается cmpxchg16b
. (Будущая версия стандарта С++ может поддерживать аналогичную функциональность с шаблоном класса std::atomic_view
.)
Это меня спрашивает:
Что произойдет, если операция атома CAS при большем размере данных изменит ситуацию, произошедшую в результате операции атома в том же месте памяти, но используя меньший размер данных?
Например, предположим, что мы имеем:
struct uint128_type {
uint64_t x;
uint64_t y;
} __attribute__ ((aligned (16)));
И предположим, что у нас есть общая переменная типа uint128_type
, например:
uint128_type Foo;
Теперь предположим, что Thread A:
Foo expected = { 0, 0 };
Foo desired = { 100, 100 };
int result = __atomic_compare_exchange(
&Foo,
&expected,
&desired,
0,
__ATOMIC_SEQ_CST
);
И Thread B делает:
uint64_t expected = 0;
uint64_t desired = 500;
int result = __atomic_compare_exchange(
&Foo.x,
&expected,
&desired,
0,
__ATOMIC_SEQ_CST
);
Что произойдет, если Thread A 16-байтовый CAS произойдет до Thread B 8 байта CAS (или наоборот)? Не работает ли CAS как обычно? Это даже определенное поведение? Возможно ли это "сделать правильную вещь" на типичных архитектурах, таких как x86_64, которые поддерживают 16b CAS?
Изменить: чтобы быть ясным, так как это вызывает путаницу, я не спрашиваю, определено ли выше поведение по стандарту С++. Очевидно, что все функции __atomic_
* являются расширениями GCC. (Однако будущим стандартам С++, возможно, придется определять такие вещи, если std::atomic_view
становится стандартизованным.) Я прошу более подробно рассказать о семантике атомных операций на типичном современном оборудовании. Например, если код x86_64 имеет 2 потока, выполняющие атомарные операции по одному и тому же адресу памяти, но один поток использует CMPXCHG8b
, а другой использует cmpxchg16b
, так что один атомный CAS на одном слове, а другой выполняет атомный CAS на двойном слове, как определяется семантика этих операций? Более конкретно, будет <<212 > сбой, поскольку он отмечает, что данные мутировали из ожидаемого значения из-за предыдущего CMPXCHG8b
?
Другими словами, можно безопасно использовать две разные операции CAS, используя два разных размера данных (но одинаковый адрес начальной памяти) для синхронизации между потоками?