Что такое атомарный API C++11, эквивалентный ```__asm__ volatile(::: memory)```

В кодовой базе есть макрос COMPILER_BARRIER, определенный как __asm__ volatile("" ::: "memory"). Цель макроса — предотвратить переупорядочивание операций чтения и записи через барьер компилятором. Обратите внимание, что это явно барьер компилятора, а не барьер памяти на уровне процессора.

В нынешнем виде это довольно переносимо, так как в AssemblerTemplate нет реальных инструкций по сборке, только volatile и memory клобер. Таким образом, пока компилятор соблюдает синтаксис расширенного ассемблера GCC, он должен работать нормально. Тем не менее, мне любопытно, как правильно выразить это в атомарном API С++ 11, если это возможно.

Следующее казалось правильным: atomic_signal_fence(memory_order_acq_rel);.

Мои рассуждения таковы:

  • Из <atomic> API только atomic_signal_fence и atomic_thread_fence не нуждаются в адресе памяти для работы.
  • atomic_thread_fence влияет на порядок памяти, который нам не нужен для барьера компилятора.
  • Клоббер memory в расширенной версии Asm не делает различий между чтением и записью, поэтому может показаться, что нам нужны семантика как получения, так и освобождения, поэтому memory_order_acq_rel, по-видимому, требуется как минимум.
  • memory_order_seq_cst кажется ненужным, поскольку нам не требуется общий порядок потоков — нас интересует только последовательность инструкций внутри текущего потока.

Можно ли выразить эквивалент __asm__ volatile("" ::: "memory") полностью переносимым с помощью C++11 atomics API? Если да, то является ли atomic_signal_fence правильным API для использования? Если да, то какой аргумент порядка памяти подходит/требуется здесь?

Или я здесь в сорняках, и есть лучший способ подойти к этому?


person acm    schedule 26.07.2016    source источник
comment
atomic_signal_fence гарантирует только порядок между потоком и обработчиком сигналов, работающим в том же потоке. Точно так же atomic_thread_fence применяется только к порядку между потоками. Если вы пытаетесь гарантировать порядок между двумя другими контекстами, то ни один из них не является переносимым. Например, в Windows atomic_signal_fence ничего делать не нужно, потому что Windows не поддерживает асинхронные сигналы.   -  person Ross Ridge    schedule 26.07.2016
comment
@RossRidge - мне было немного странно использовать atomic_signal_fence, потому что, как вы заметили, здесь нет никаких сигналов. Но это было единственное, что сработало, согласно моей схеме выше. Я не видел в стандарте никакого языка, который позволил бы исключить вызов atomic_signal_fence, если в реализации не было асинхронных сигналов. В 28.9.7 стандарта C++14 указано, что оптимизация компилятора и переупорядочивание загрузок и хранилищ запрещаются так же, как с atomic_thread_fence, но инструкции аппаратного ограждения... не выдаются.   -  person acm    schedule 26.07.2016
comment
Это информативное (ненормативное) примечание, оно не накладывает ограничений на реализацию. Стандарт не предоставляет никакого языка, который позволил бы вам полагаться на то, что он является чем-то большим, чем эквивалент atomic_thread_fence(order), за исключением того, что результирующие ограничения упорядочения устанавливаются только между потоком и обработчиком сигнала, выполняемым в том же потоке. Также обратите внимание, что atomic_thread_fence определяется в терминах атомарных операций над атомарными объектами, как это определено стандартом. Поэтому, если вы не используете типы std::atomic, ни одна из функций не будет работать.   -  person Ross Ridge    schedule 26.07.2016


Ответы (2)


__asm__ volatile("" ::: "memory") даже не является полным барьером для компилятора; он только принудительно упорядочивает загрузки/сохранения для объектов, адреса которых потенциально доступны для блока asm, который не будет включать локальные переменные, для которых компилятор может отслеживать, что адрес не утекает. Например, memset(password, 0, len);, за которым следует __asm__ volatile("" ::: "memory");, может фактически не обнулить память, используемую password[].

Это можно исправить, передав адреса таких объектов в качестве входных данных ассемблерному блоку, но я не вижу идеального эквивалента с atomic_signal_fence. Самое близкое, что вы, вероятно, могли бы сделать, это сохранить адрес объекта во внешнем объекте-указателе volatile (будьте осторожны, чтобы сделать указатель, а не указатель на тип, volatile-квалифицированным), а затем atomic_signal_fence должен был бы предположить, что он может быть доступным из обработчика сигнала.

person R.. GitHub STOP HELPING ICE    schedule 26.07.2016
comment
Это очень полезное наблюдение. Это заставляет меня задаться вопросом, действительно ли макрос делает то, что было задумано! - person acm; 26.07.2016
comment
@acm и R.: наблюдение из другого потока или обработчика сигналов было бы невозможно без UB для объектов, адреса которых не вышли из функции. Вот почему им не нужно давать адрес и пропускать через барьер компилятора. Я думаю, что atomic_signal_fence GCC реализован с точки зрения asm("":::"memory") или, по крайней мере, внутренне эквивалентен внутри компилятора. - person Peter Cordes; 14.10.2019
comment
Или вы просто говорите, что atomic_signal_fence не имеет механизма передачи в него дополнительных адресов локалов? Конечно, но это эквивалентно макросу без аргументов для asm("":::"memory"). - person Peter Cordes; 14.10.2019

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

Вы, кажется, смешиваете разные вопросы.

Семантика получения и освобождения может создать ограничение как для чтения, так и для записи:

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

Однако это очень упрощенное объяснение. Атомные барьеры C++ — это барьеры для атомов. Они работают в координации с атомарными объектами. Конечно, вызов барьера потока может создавать код сам по себе, но этот код можно переупорядочить с помощью некоторых неатомарных операций.

person curiousguy    schedule 17.12.2019