Атомный 16-байтовый режим считывается на процессорах x64
Мне нужно читать/писать 16 байт атомарно. Я пишу только с использованием cmpxchg16, который доступен на всех процессорах x64, за исключением того, что я думаю для одного неясного AMD.
Теперь вопрос заключается в выровненных 16 байтовых значениях, только когда-либо измененных с использованием cmpxchg16 (который действует как полный барьер памяти), возможно ли когда-либо прочитать 16-байтовое местоположение, что половина старых данных и половина новых данных?
Пока я читаю инструкцию SSE (поэтому поток не может быть прерван в середине чтения), я думаю, что это невозможно (даже в многопроцессорных системах numa) для чтения, чтобы увидеть несогласованные данные. Я думаю, что он должен быть атомарным.
Я исхожу из предположения, что при выполнении cmpxchg16 он изменяет 16 байтов атомарно, а не записывая два 8-байтовых блока с возможностью для других потоков читать между ними (честно говоря, я не вижу, как это могло бы быть если он не был атомарным.)
Я прав? Если я ошибаюсь, есть ли способ сделать атомный 16-байтовый текст без использования блокировки?
Примечание. Здесь есть пара вопросов но они не имеют отношения к случаю, когда записи выполняются только с cmpxchg16, поэтому я считаю, что это отдельный, неотвеченный вопрос.
Изменить: На самом деле, я думаю, что мои рассуждения были ошибочными. Инструкция загрузки SSE может выполняться как два 64-битных чтения, и может быть возможно, чтобы cmpxchg16 выполнялся между двумя чтениями другим процессором.
Ответы
Ответ 1
typedef struct
{
unsigned __int128 value;
} __attribute__ ((aligned (16))) atomic_uint128;
unsigned __int128
atomic_read_uint128 (atomic_uint128 *src)
{
unsigned __int128 result;
asm volatile ("xor %%rax, %%rax;"
"xor %%rbx, %%rbx;"
"xor %%rcx, %%rcx;"
"xor %%rdx, %%rdx;"
"lock cmpxchg16b %1" : "=A"(result) : "m"(*src) : "rbx", "rcx");
return result;
}
Это должно сделать трюк. Typedef обеспечивает правильное выравнивание. Для cmpxchg16b нужны данные, которые должны быть выровнены по 16-байтовой границе.
cmpxchg16b будет тестировать, если *src
содержит нуль и записывает нуль, если это так (nop). В любом случае правильное значение будет стоять в RAX: впоследствии RDX.
Приведенный выше код оценивается примерно так же, как
push %rbx
xor %rax,%rax
xor %rbx,%rbx
xor %rcx,%rcx
xor %rdx,%rdx
lock cmpxchg16b (%rdi)
pop %rbx
retq
Ответ 2
В соответствии со ссылками http://siyobik.info/main/reference/instruction/CMPXCHG8B%2FCMPXCHG16B CMPXCHG16
по умолчанию не является атомарным, но может быть сделан атомом, используя LOCK
http://siyobik.info/main/reference/instruction/LOCK
Это означает, что по умолчанию данные могут быть изменены на этапах чтения и записи. Блокировка делает как чтение, так и запись атомом.