@Showvars
Calling this function on an atomic object that has already been initialized (either on construction or by calling this function earlier) causes undefined behavior
If obj was not default-constructed, the behavior is undefined.
И тут возникает вопрос. Допустим у нас есть структура, в которой есть atomic переменная. Выделяя память и инициализируя переменную, мы можем в дальнейшем использовать ее не сталкиваясь с undefined behavior. Но что если таких структур у нас много и их расположение в заранее определенной области памяти может меняться при работе?
Например, я хочу сделать стек, где элементы этого стека будут иметь заранее неизвестный размер (заголовок и следующий за ним произвольный массив). Каждый раз добавляя новый элемент (подчеркну, что в заранее выделенной памяти фиксированного размера), мы должны инициализировать атомарную переменную, а при удалении мы с ней ничего делать не должны, ибо соответствующая функция в стандарте отсутствует. В процессе работы и многократного создания/удаления элементов стека, мы каждый раз инициализируем новую атомарную переменную, которая может быть в памяти где угодно, включая те адреса, где раньше лежала старая переменная. Выходит мы можем инициализировать переменную дважды, а тогда, согласно стандарту, получим undefined behavior. Даже если мы исключаем возможность race condition, безопасно ли использовать атомарные переменные таким способом?
Честно говоря, я совершенно не понимаю, как происходит инициализация таких переменных в современных процессорах и компиляторах. Вероятно для некоторых случаев никакой специальной инициализации и вовсе не нужно, а для некоторых она создает блокировки.
Решения вопроса 1
@res2001
Но на них накладываются некоторые ограничения:
1. выравнивание переменной — переменная должна находится по адресу обычно кратному размеру переменной. Если не будет правильного выравнивания, то intelовские процы могут сделать 2 операции чтения, что уже не будет атомарной операцией. Другие процы могут сгенерировать какое-нибудь исключение.
2. атомарная переменная всегда volatile — т.е. компилятор не может ее кэшировать в регистре, всегда идет обращение к памяти.
Не все атомарные переменные в std действительно атомарны. Это проверяется с помощью atomic_is_lock_free(). Атомарность зависит от платформы. Например на x86 int64 — не атомарна из-за ограничений процессора. Тогда как на 32 битных АРМах она вполне атомарна.
При обычном чтении/записи атомарных переменных (с упорядочиванием памяти memory_order_seq_cst) происходит синхронизация кэшей ядер — из-за чего атомарные операции «дороже» не атомарных. Но этот процесс на разных архитектурах стоит по разному. Для синхронизации кэшей есть отдельные ассемблерные инструкции.
В целом все undefined behavior для атомарных переменных в std связаны с:
1. возможностью объявить атомарным любой класс
2. не для всех встроенных типов на конкретной платформе гарантируется реальная атомарность (atomic_is_lock_free).
3. если вы не будете соблюдать ограничения, то же ничего гарантировать нельзя.
Так что особо пугаться undefined behavior не стоит. Просто соблюдайте ограничения, не используйте атомарные классы и удостоверьтесь, что встроенные типы на вашей платформе действительно атомарны.
1
комментарий
Ответы на вопрос 0