Является ли стандартный С++ 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.