2016-07-26 20 views
6

Кодовая база имеет макрос COMPILER_BARRIER, определенный как __asm__ volatile("" ::: "memory"). Цель макроса состоит в том, чтобы предотвратить перекомпилирование компилятора при чтении и записи через барьер. Обратите внимание, что это явно барьер компилятора, а не уровень памяти уровня памяти.Что такое атомный API C++ 11, эквивалентный `` `__asm__ volatile (" ":::" memory ")` ``

Как есть, это довольно портативно, поскольку в сборке AssemblerTemplate нет фактических инструкций по сборке, только volatile и clobber memory. Итак, до тех пор, пока компилятор отличает синтаксис расширенного Asm от GCC, он должен работать нормально. Тем не менее, мне любопытно, как правильно выразить это, если это возможно, в API-интерфейсе C++ 11 atomics.

Возможно, что это была правильная идея: 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") полностью портативно с помощью API атомизации C++ 11? Если да, то atomic_signal_fence правильный API для использования? Если да, то какой аргумент порядка памяти подходит/необходим здесь?

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

+1

'atomic_signal_fence' гарантирует только порядок между потоком и обработчиком сигнала, работающим в том же потоке. Аналогично, 'atomic_thread_fence' применяется только к упорядочению между потоками. Если вы пытаетесь гарантировать заказ между двумя другими контекстами, то ни один из них не переносится. Например, в Windows 'atomic_signal_fence' ничего не нужно делать, потому что Windows не поддерживает асинхронные сигналы. –

+0

@RossRidge - Мне было немного странно использовать atom_signal_fence, потому что, как вы указываете, здесь нет сигналов. Но это было единственное, что «сработало», по моей схеме выше. Я не видел ни одного языка в стандарте, хотя это позволило бы вызывать atom_signal_fence, если бы реализация не имела асинхронных сигналов. В 28.9.7 стандарта C++ 14 указано, что «оптимизация компилятора и переупорядочивание нагрузок и хранилищ запрещены так же, как и с atomic_thread_fence, но инструкции по аппаратным ограждениям ...». – acm

+0

Это информативная (ненормативная) заметка, она не создает ограничений на реализацию. В стандарте не предусмотрен какой-либо язык, который позволил бы вам зависеть от него как нечто большее, чем «эквивалентное atom_thread_fence (order)», за исключением того, что результирующие ограничения упорядочения устанавливаются только между потоком и обработчиком сигнала, выполненным в том же потоке » , Заметим также, что 'atomic_thread_fence' определяется в терминах атомных операций над атомными объектами, как определено стандартом. Поэтому, если вы не используете типы 'std :: atomic', то ни одна из функций не будет работать. –

ответ

3

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

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

+0

Это очень полезное наблюдение. Это заставляет меня задаться вопросом, действительно ли макрос делает то, что было предназначено! – acm