Является ли стандартный С++ 11 гарантией того, что `volatile atomic <T>` имеет как семантику (volatile + atomic)?

Как известно, std::atomic и volatile - это разные вещи.

Есть два основных отличия:

  • Две оптимизации могут быть для std::atomic<int> a;, но не могут быть для volatile int a;:

    • плавные операции: a = 1; a = 2; может быть заменен компилятором на a = 2;
    • постоянное распространение: a = 1; local = a; может быть заменено компилятором на a = 1; local = 1;
  • Переупорядочение обычных чтений/операций записи через атомарные/изменчивые операции:

    • для volatile int a; любые операции вольтамплитного чтения/записи не могут быть переупорядочены. Но близкие обычные чтения/записи все еще могут быть переупорядочены вокруг изменчивых операций чтения/записи.
    • для std::atomic a; переупорядочение близлежащих обычных чтений/записей, ограниченных на основе барьера памяти, используемого для атомной операции a.load(std::memory_order_...);

т.е. volatile не вводят забор памяти, но std::atomic может это сделать.

Как хорошо описано в статье:

Например, std::atomic следует использовать для параллельных многопоточных программ (CPU-Core ↔ CPU-Core), но volatile следует использовать для доступа к Mamory Mapped Regions на устройствах (CPU-Core ↔ Устройство).


Но при необходимости оба имеют необычную семантику и имеют любые или все гарантии атомарности и/или заказа, необходимые для незакрепленного кодирования, т.е. если требуется volatile std::atomic<>, требуют по нескольким причинам:

  • упорядочение: чтобы предотвратить переупорядочение обычных чтений/записей, например, для чтения из CPU-RAM, на которые были записаны данные с помощью DMA-контроллера устройства

Например:

char cpu_ram_data_written_by_device[1024];
device_dma_will_write_here( cpu_ram_data_written_by_device );

// physically mapped to device register
volatile bool *device_ready = get_pointer_device_ready_flag();

//... somewhere much later
while(!device_ready); // spin-lock (here should be memory fence!!!)
for(auto &i : cpu_ram_data_written_by_device) std::cout << i;

Пример:

char cpu_ram_data_will_read_by_device[1024];
device_dma_will_read_it( cpu_ram_data_written_by_device );

// physically mapped to device register
volatile bool *data_ready = get_pointer_data_ready_flag();

//... somewhere much later
for(auto &i : cpu_ram_data_will_read_by_device) i = 10;
data_ready=true; //spilling cpu_ram_data_will_read_by_device to RAM, should be memory fence
  • atomic: гарантировать, что волатильная операция будет атомарной, т.е. она будет состоять из одной операции, а не из нескольких, т.е. одна 8-байтовая операция вместо двух 4-байтовых операций

Для этого Херб Саттер сказал о volatile atomic<T>, 08 января 2009: http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484?pgno=2

Наконец, чтобы выразить переменную, которая имеет необычную семантику и имеет любые или все гарантии атомарности и/или заказа, необходимые для без блокировки, только стандарт стандарта ISO С++ 0x обеспечивает прямой способ его произнести: летучий атомный.

Но существуют ли современные стандарты С++ 11 (не С++ 0x draft), С++ 14 и С++ 17 гарантируют, что volatile atomic<T> имеет как семантику (volatile + atomic)

Обеспечивает ли volatile atomic<T> самые строгие гарантии как от летучих, так и от атомных?

  • Как и в volatile: предотвращает плавкие операции и постоянное распространение, как описано в начале вопроса
  • Как и в std::atomic: вводит забор памяти, чтобы обеспечить упорядочение, проливание и атомарное.

И можем ли мы сделать reinterpret_cast от volatile int *ptr; до volatile std::atomic<int>*?

Ответы

Ответ 1

Да, это так.

Раздел 29.6.5, "Требования к операциям с атомными типами"

Многие операции являются нестабильными. Семантика "volatile as register" не изменилась в стандарте. Эта квалификация означает, что волатильность сохраняется при применении этих операций к изменчивым объектам.

Я проверил рабочие проекты с 2008 по 2016 год, и тот же текст во всех них. Поэтому он должен применять С++ 11, С++ 14 и С++ 17.