Сравнение и своп: синхронизация с использованием разных размеров данных

Используя встроенные 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, используя два разных размера данных (но одинаковый адрес начальной памяти) для синхронизации между потоками?

Ответ 1

Сначала происходит одно или другое, и каждая операция выполняется в соответствии с его собственной семантикой.

В процессорах x86 обеим операциям потребуется блокировка в той же строке кэша, которая была сохранена на протяжении всей операции. Таким образом, в зависимости от того, какой из этих блокировок сначала не увидит никаких эффектов второй операции, и в зависимости от того, какой из них получит этот второй секунда, будут видны все эффекты первой операции. Семантика обеих операций будет полностью соблюдена.

Другое аппаратное обеспечение может достичь этого результата другим способом, но если он не достигнет этого результата, он сломается, если только он не указывает, что он имеет ограничение.

Ответ 2

Атомные данные, в конечном счете, будут расположены где-то в памяти и все обращения к нему (или к соответствующим кэшам, когда операции будут атомарными) будут сериализованы. Поскольку операция CAS должна быть атомарной, она будет выполняться в целом или вообще не будет.

При этом одна из операций будет успешной, вторая не удастся. Порядок не является детерминированным.

Из x86 Инструкция Установить ссылку:

Эта команда может использоваться с префиксом LOCK, чтобы позволить инструкции выполняться атомарно. Чтобы упростить интерфейс к шине процессоров, операнд назначения получает цикл записи без учета результата сравнения. Операнд назначения записывается обратно, если сравнение не выполняется; в противном случае исходный операнд записывается в пункт назначения. (Процессор никогда не производит блокировку чтения без создания заблокированной записи.)

Очевидно, что оба потока попытаются заблокировать запись после заблокированного чтения (при использовании с префиксом LOCK, то есть), что означает, что только один из них преуспеет в выполнении CAS, другой будет читать уже измененное значение.

Ответ 3

Определение атома вряд ли изменится, акцент мой

При параллельном программировании операция (или набор операций) является атомарной, линеаризуемо, неделимо или без прерывания, если оно остальная часть системы должна произойти мгновенно. Атомность - это гарантия изоляции от параллельных процессов.

Ваш вопрос спрашивает...

Что произойдет, если наблюдает атомная операция CAS при большем размере данных изменение, которое произошло при работе атома в одной и той же памяти местоположение, но с использованием меньшего размера данных?

По определению никакие две перекрывающиеся области памяти, модифицированные с использованием атомных операций, могут mutate одновременно, то есть две операции должны выполняться линейно или их не атомные.

Ответ 4

Аппаратное обеспечение обычно очень консервативно при проверке конфликтов между потенциально конфликтными атомарными операциями. Возможно, даже произойдет, что две операции CAS с двумя совершенно разными, неперекрывающимися диапазонами адресов могут быть обнаружены для конфликта друг с другом.