Атомика C11 и C ++ 11: семантика получения-выпуска и барьеры памяти

Я использую атомику C11 * для управления перечислением состояний между несколькими потоками. Код выглядит следующим образом:

static _Atomic State state;

void setToFoo(void)
{
    atomic_store_explicit(&state, STATE_FOO, memory_order_release);
}

bool stateIsBar(void)
{
    return atomic_load_explicit(&state, memory_order_acquire) == STATE_BAR;
}

Это собирает (для ARM Cortex-M4) в:

<setToFoo>:
   ldr  r3, [pc, #8]
   dmb  sy ; Memory barrier
   movs r2, #0
   strb r2, [r3, #0] ; store STATE_FOO
   bx   lr
   .word    0x00000000

<stateIsBar>:
  ldr   r3, [pc, #16]
  ldrb  r0, [r3, #0] ; load state
  dmb   sy ; Memory barrier
  sub.w r0, r0, #2 ; Comparison and return follows
  clz   r0, r0
  lsrs  r0, r0, #5
  bx    lr
  .word 0x00000000

Почему ограждения ставятся до выпуска и после приобретения? Моя ментальная модель предполагала, что барьер будет установлен после после выпуска (для «распространения» сохраняемой переменной и всех других хранилищ на другие потоки) и до получения ( чтобы получить все предыдущие хранилища из других потоков).


* Хотя этот конкретный пример приведен в C11, ситуация идентична в C ++ 11, поскольку оба используют одни и те же концепции (и даже одни и те же перечисления), когда дело доходит до упорядочения памяти. gcc и g++ в этой ситуации испускают один и тот же машинный код. См. http://en.cppreference.com/w/c/atomic/memory_order и http://en.cppreference.com/w/cpp/atomic/memory_order


person Matt Kline    schedule 22.10.2015    source источник
comment
В stateIsBar(void), вы имели в виду return это выражение? Потому что, как написано, это функция без оператора return, которая не возвращает void. Он должен скомпилироваться в одну инструкцию возврата (на самом деле, барьер памяти по-прежнему необходим, но не нагрузка).   -  person Peter Cordes    schedule 23.10.2015
comment
Правильно - спасибо, что поймали опечатку.   -  person Matt Kline    schedule 23.10.2015


Ответы (1)


Забор памяти перед магазином должен гарантировать, что магазин не будет заказан раньше других магазинов. Точно так же ограждение памяти после чтения гарантирует, что чтение не будет упорядочено после любых последующих чтений. Когда вы объединяете их, создается отношение синхронизируется с между записью и чтением.

T1: on-deps(A) -> fence -> write(A)

T2: read(A) -> fence -> deps-on(A)

чтение (A) происходит до того, как deps-on (A)

запись (A) происходит после включения (A)

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

Еще несколько возможных прочтений ...

person Jason    schedule 23.10.2015