32 Atomic operations library [atomics]

32.6 Class template atomic [atomics.types.generic]

namespace std {
  template <class T> struct atomic {
    using value_type = T;
    static constexpr bool is_always_lock_free = implementation-defined;
    bool is_lock_free() const volatile noexcept;
    bool is_lock_free() const noexcept;
    void store(T, memory_order = memory_order_seq_cst) volatile noexcept;
    void store(T, memory_order = memory_order_seq_cst) noexcept;
    T load(memory_order = memory_order_seq_cst) const volatile noexcept;
    T load(memory_order = memory_order_seq_cst) const noexcept;
    operator T() const volatile noexcept;
    operator T() const noexcept;
    T exchange(T, memory_order = memory_order_seq_cst) volatile noexcept;
    T exchange(T, memory_order = memory_order_seq_cst) noexcept;
    bool compare_exchange_weak(T&, T, memory_order, memory_order) volatile noexcept;
    bool compare_exchange_weak(T&, T, memory_order, memory_order) noexcept;
    bool compare_exchange_strong(T&, T, memory_order, memory_order) volatile noexcept;
    bool compare_exchange_strong(T&, T, memory_order, memory_order) noexcept;
    bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst) volatile noexcept;
    bool compare_exchange_weak(T&, T, memory_order = memory_order_seq_cst) noexcept;
    bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst) volatile noexcept;
    bool compare_exchange_strong(T&, T, memory_order = memory_order_seq_cst) noexcept;

    atomic() noexcept = default;
    constexpr atomic(T) noexcept;
    atomic(const atomic&) = delete;
    atomic& operator=(const atomic&) = delete;
    atomic& operator=(const atomic&) volatile = delete;
    T operator=(T) volatile noexcept;
    T operator=(T) noexcept;
  };
}

Аргумент шаблона для T должен быть легко копируемым ([basic.types]). [ Note: Аргументы типа, которые также не могут быть статически инициализированы, могут быть трудными для использования. ]end note

Специализацияatomic<bool> - это структура стандартного макета.

[ Note: Представление атомарной специализации не обязательно должно иметь тот же размер, что и соответствующий тип аргумента. По возможности, специализации должны иметь одинаковый размер, так как это сокращает усилия, необходимые для переноса существующего кода. ]end note

32.6.1 Operations on atomic types [atomics.types.operations]

[ Note: Многие операции квалифицируются как нестабильные. Семантика «изменчивый как регистр устройства» в стандарте не изменилась. Эта квалификация означает, что изменчивость сохраняется при применении этих операций к изменчивым объектам. Это не означает, что операции с энергонезависимыми объектами становятся нестабильными. ]end note

atomic() noexcept = default;

Effects: Оставляет атомарный объект в неинициализированном состоянии. [ Note: Эта семантика обеспечивает совместимость с C. ] end note

constexpr atomic(T desired) noexcept;

Effects: Инициализирует объект значениемdesired. Инициализация - это не атомарная операция ([intro.multithread]). [ Note: Можно получить доступ кA гонке атомарных объектов с ее построением, например, передав адрес только что созданного объектаA другому потоку с помощью memory_­order_­relaxed операций с подходящей переменной атомарного указателя, а затем немедленно обращаясьA к принимающему потоку. Это приводит к неопределенному поведению. ] end note

#define ATOMIC_VAR_INIT(value) see below

Макрос расширяется до последовательности токенов, подходящей для постоянной инициализации атомарной переменной со статической продолжительностью хранения типа, совместимого с инициализациейvalue. [ Note: Эта операция может потребоваться для инициализации блокировок. ] Параллельный доступ к инициализируемой переменной, даже через атомарную операцию, составляет гонку данных. [end noteExample:

atomic<int> v = ATOMIC_VAR_INIT(5);

end example]

static constexpr bool is_always_lock_free = implementation-defined;

static Элемент данныхis_­always_­lock_­free является ,true если операции в атомарном типе являются всегда безблокировочными, и вfalse противном случае. [ Note: Значениеis_­always_­lock_­free согласуется со значением соответствующегоATOMIC_­..._­LOCK_­FREE макроса, если он определен. ] end note

bool is_lock_free() const volatile noexcept; bool is_lock_free() const noexcept;

Returns:true если операции объекта не блокируются, вfalse противном случае. [ Note: Возвращаемое значение функции-is_­lock_­free члена согласуется со значениемis_­always_­lock_­free для того же типа. ] end note

void store(T desired, memory_order order = memory_order_seq_cst) volatile noexcept; void store(T desired, memory_order order = memory_order_seq_cst) noexcept;

Requires: order Аргумент не может бытьmemory_­order_­consume, memory_­order_­acquireи неmemory_­order_­acq_­rel.

Effects: Атомно заменяет значение, на которое указывает,this на значениеdesired. На память влияет значение order.

T operator=(T desired) volatile noexcept; T operator=(T desired) noexcept;

Effects: Эквивалент:store(desired).

Returns:desired.

T load(memory_order order = memory_order_seq_cst) const volatile noexcept; T load(memory_order order = memory_order_seq_cst) const noexcept;

Requires: order Аргумент не должен бытьmemory_­order_­release ниmemory_­order_­acq_­rel.

Effects: На память влияет значениеorder.

Returns: Атомарно возвращает значение, на которое указываетthis.

operator T() const volatile noexcept; operator T() const noexcept;

Effects: Эквивалентен:return load();

T exchange(T desired, memory_order order = memory_order_seq_cst) volatile noexcept; T exchange(T desired, memory_order order = memory_order_seq_cst) noexcept;

Effects: Атомно заменяет значение , на который указываетthis сdesired. На память влияет значениеorder. Эти операции являются атомарными операциями чтения-изменения-записи ([intro.multithread]).

Returns: Атомарно возвращает значение, на которое указываетthis непосредственно перед эффектами.

bool compare_exchange_weak(T& expected, T desired, memory_order success, memory_order failure) volatile noexcept; bool compare_exchange_weak(T& expected, T desired, memory_order success, memory_order failure) noexcept; bool compare_exchange_strong(T& expected, T desired, memory_order success, memory_order failure) volatile noexcept; bool compare_exchange_strong(T& expected, T desired, memory_order success, memory_order failure) noexcept; bool compare_exchange_weak(T& expected, T desired, memory_order order = memory_order_seq_cst) volatile noexcept; bool compare_exchange_weak(T& expected, T desired, memory_order order = memory_order_seq_cst) noexcept; bool compare_exchange_strong(T& expected, T desired, memory_order order = memory_order_seq_cst) volatile noexcept; bool compare_exchange_strong(T& expected, T desired, memory_order order = memory_order_seq_cst) noexcept;

Requires: failure Аргумент не должен бытьmemory_­order_­release ни memory_­order_­acq_­rel.

Effects: Извлекает значение в форматеexpected. Затем он атомарно сравнивает содержимое памяти, на которую указываетthis на равенство с тем, что было ранее получено изexpected, и, если истинно, заменяет содержимое памяти, на которое указывает, на то,this что вdesired. Если и только если сравнение истинно, память затрагивается в соответствии со значениемsuccess, а если сравнение ложно, память затрагивается в соответствии со значениемfailure. Когда предоставляется только одинmemory_­order аргумент, значениеsuccess равноorder, а значение failure равно,order за исключением того, что значениеmemory_­order_­acq_­rel должно быть заменено значением,memory_­order_­acquire а значение memory_­order_­release должно быть заменено значением memory_­order_­relaxed. Если и только если сравнение ложно, то после атомарной операции содержимое памяти вexpected заменяется значением, считанным из памяти, на которую указываетthis во время атомарного сравнения. Если операция возвращаетсяtrue, эти операции являются атомарными операциями чтения-изменения-записи ([intro.multithread]) в памяти, на которую указываетthis. В противном случае эти операции представляют собой операции атомарной загрузки в эту память.

Returns: Результат сравнения.

[ Note: Например, эффект compare_­exchange_­strong есть

if (memcmp(this, &expected, sizeof(*this)) == 0)
  memcpy(this, &desired, sizeof(*this));
else
  memcpy(expected, this, sizeof(*this));

end note] [ Example: Ожидаемое использование операций сравнения и обмена следующее. Операции сравнения и обмена обновятся,expected когда потребуется еще одна итерация цикла.

expected = current.load();
do {
  desired = function(expected);
} while (!current.compare_exchange_weak(expected, desired));

end example] [ Example: Поскольку ожидаемое значение обновляется только в случае ошибки, код, освобождающий память, содержащуюexpected значение, в случае успеха будет работать. Например, вставка заголовка списка будет действовать атомарно и не приведет к гонке данных в следующем коде:

do {
  p->next = head; // make new list node point to the current head
} while (!head.compare_exchange_weak(p->next, p)); // try to insert

end example]

Реализации должны гарантировать, что слабые операции сравнения и обмена не будут последовательно возвращаться,false если только атомарный объект не имеет значение, отличное от атомарного объекта,expected или не происходит одновременных модификаций атомарного объекта.

Remarks: Слабая операция сравнения и обмена может привести к ложному сбою. То есть, даже когда содержимое памяти, на которое ссылаетсяexpected иthis равны, оно может вернутьсяfalse и сохранить обратно вexpected то же содержимое памяти, которое было изначально там. [ Note: Этот ложный сбой позволяет реализовать сравнение и обмен на более широком классе машин, например, на машинах с фиксированной загрузкой и условием сохранения. Следствием ложного отказа является то, что почти все случаи использования слабого сравнения и обмена будут замкнутыми.

Когда в цикле сравнения и обмена, слабая версия даст лучшую производительность на некоторых платформах. Когда для слабого сравнения и обмена требуется петля, а для сильной - нет, предпочтительнее сильное. ]end note

[ Note: Вmemcpy иmemcmp семантике операций сравнения и обмена-может привести к неудачным сравнениям для значений , которые сравнивают с равным ,operator== если основным типа имеет биты заполнения, ловушки биты или альтернативные представления одного и то же значение. Таким образом,compare_­exchange_­strong следует использовать с особой осторожностью. С другой стороны,compare_­exchange_­weak должно быстро сходиться. ] end note

32.6.2 Specializations for integers [atomics.types.int]

Есть специализацииatomic шаблона для целочисленных типов char, signed char, unsigned char, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, char16_­t, char32_­t, wchar_­t, и любых другие типов необходимо в определениях типов в заголовке<cstdint>. Для каждого такого интегрального типаintegralспециализация atomic<integral> предоставляет дополнительные атомарные операции, соответствующие целочисленным типам. [ Note: О специализацииatomic<bool>см[atomics.types.generic]. ]end note

namespace std {
  template <> struct atomic<integral> {
    using value_type = integral;
    using difference_type = value_type;
    static constexpr bool is_always_lock_free = implementation-defined;
    bool is_lock_free() const volatile noexcept;
    bool is_lock_free() const noexcept;
    void store(integral, memory_order = memory_order_seq_cst) volatile noexcept;
    void store(integral, memory_order = memory_order_seq_cst) noexcept;
    integral load(memory_order = memory_order_seq_cst) const volatile noexcept;
    integral load(memory_order = memory_order_seq_cst) const noexcept;
    operator integral() const volatile noexcept;
    operator integral() const noexcept;
    integral exchange(integral, memory_order = memory_order_seq_cst) volatile noexcept;
    integral exchange(integral, memory_order = memory_order_seq_cst) noexcept;
    bool compare_exchange_weak(integral&, integral,
                               memory_order, memory_order) volatile noexcept;
    bool compare_exchange_weak(integral&, integral,
                               memory_order, memory_order) noexcept;
    bool compare_exchange_strong(integral&, integral,
                                 memory_order, memory_order) volatile noexcept;
    bool compare_exchange_strong(integral&, integral,
                                 memory_order, memory_order) noexcept;
    bool compare_exchange_weak(integral&, integral,
                               memory_order = memory_order_seq_cst) volatile noexcept;
    bool compare_exchange_weak(integral&, integral,
                               memory_order = memory_order_seq_cst) noexcept;
    bool compare_exchange_strong(integral&, integral,
                               memory_order = memory_order_seq_cst) volatile noexcept;
    bool compare_exchange_strong(integral&, integral,
                               memory_order = memory_order_seq_cst) noexcept;
    integral fetch_add(integral, memory_order = memory_order_seq_cst) volatile noexcept;
    integral fetch_add(integral, memory_order = memory_order_seq_cst) noexcept;
    integral fetch_sub(integral, memory_order = memory_order_seq_cst) volatile noexcept;
    integral fetch_sub(integral, memory_order = memory_order_seq_cst) noexcept;
    integral fetch_and(integral, memory_order = memory_order_seq_cst) volatile noexcept;
    integral fetch_and(integral, memory_order = memory_order_seq_cst) noexcept;
    integral fetch_or(integral, memory_order = memory_order_seq_cst) volatile noexcept;
    integral fetch_or(integral, memory_order = memory_order_seq_cst) noexcept;
    integral fetch_xor(integral, memory_order = memory_order_seq_cst) volatile noexcept;
    integral fetch_xor(integral, memory_order = memory_order_seq_cst) noexcept;

    atomic() noexcept = default;
    constexpr atomic(integral) noexcept;
    atomic(const atomic&) = delete;
    atomic& operator=(const atomic&) = delete;
    atomic& operator=(const atomic&) volatile = delete;
    integral operator=(integral) volatile noexcept;
    integral operator=(integral) noexcept;

    integral operator++(int) volatile noexcept;
    integral operator++(int) noexcept;
    integral operator--(int) volatile noexcept;
    integral operator--(int) noexcept;
    integral operator++() volatile noexcept;
    integral operator++() noexcept;
    integral operator--() volatile noexcept;
    integral operator--() noexcept;
    integral operator+=(integral) volatile noexcept;
    integral operator+=(integral) noexcept;
    integral operator-=(integral) volatile noexcept;
    integral operator-=(integral) noexcept;
    integral operator&=(integral) volatile noexcept;
    integral operator&=(integral) noexcept;
    integral operator|=(integral) volatile noexcept;
    integral operator|=(integral) noexcept;
    integral operator^=(integral) volatile noexcept;
    integral operator^=(integral) noexcept;
  };
}

Атомарные интегральные специализации представляют собой стандартные структуры. У каждого из них есть тривиальный конструктор по умолчанию и тривиальный деструктор.

Ниже приведены описания только для членов, которые отличаются от основного шаблона.

Следующие операции выполняют арифметические вычисления. Соответствие ключа, оператора и вычислений:

Таблица138 - Атомарные арифметические вычисления
key Op Вычисление key Op Вычисление
add + добавление sub - вычитание
or | побитовое включение или xor ^ побитовое исключающее или
and & побитовое и

T fetch_key(T operand, memory_order order = memory_order_seq_cst) volatile noexcept; T fetch_key(T operand, memory_order order = memory_order_seq_cst) noexcept;

Effects: Атомно заменяет значение, на которое указывает, this результатом вычисления, примененного к значению, на которое указываетthis и данноеoperand. На память влияет значениеorder. Эти операции являются атомарными операциями чтения-изменения-записи ([intro.multithread]).

Returns: Атомарно, значение, на которое указываетthis непосредственно перед эффектами.

Remarks: Для целочисленных типов со знаком арифметика определяется для использования представления с дополнением до двух. Нет неопределенных результатов.

T operator op=(T operand) volatile noexcept; T operator op=(T operand) noexcept;

Effects: Эквивалентен:return fetch_­key(operand) op operand;

32.6.3 Partial specialization for pointers [atomics.types.pointer]

namespace std {
  template <class T> struct atomic<T*> {
    using value_type = T*;
    using difference_type = ptrdiff_t;
    static constexpr bool is_always_lock_free = implementation-defined;
    bool is_lock_free() const volatile noexcept;
    bool is_lock_free() const noexcept;
    void store(T*, memory_order = memory_order_seq_cst) volatile noexcept;
    void store(T*, memory_order = memory_order_seq_cst) noexcept;
    T* load(memory_order = memory_order_seq_cst) const volatile noexcept;
    T* load(memory_order = memory_order_seq_cst) const noexcept;
    operator T*() const volatile noexcept;
    operator T*() const noexcept;
    T* exchange(T*, memory_order = memory_order_seq_cst) volatile noexcept;
    T* exchange(T*, memory_order = memory_order_seq_cst) noexcept;
    bool compare_exchange_weak(T*&, T*, memory_order, memory_order) volatile noexcept;
    bool compare_exchange_weak(T*&, T*, memory_order, memory_order) noexcept;
    bool compare_exchange_strong(T*&, T*, memory_order, memory_order) volatile noexcept;
    bool compare_exchange_strong(T*&, T*, memory_order, memory_order) noexcept;
    bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst) volatile noexcept;
    bool compare_exchange_weak(T*&, T*, memory_order = memory_order_seq_cst) noexcept;
    bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst) volatile noexcept;
    bool compare_exchange_strong(T*&, T*, memory_order = memory_order_seq_cst) noexcept;
    T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst) volatile noexcept;
    T* fetch_add(ptrdiff_t, memory_order = memory_order_seq_cst) noexcept;
    T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst) volatile noexcept;
    T* fetch_sub(ptrdiff_t, memory_order = memory_order_seq_cst) noexcept;

    atomic() noexcept = default;
    constexpr atomic(T*) noexcept;
    atomic(const atomic&) = delete;
    atomic& operator=(const atomic&) = delete;
    atomic& operator=(const atomic&) volatile = delete;
    T* operator=(T*) volatile noexcept;
    T* operator=(T*) noexcept;

    T* operator++(int) volatile noexcept;
    T* operator++(int) noexcept;
    T* operator--(int) volatile noexcept;
    T* operator--(int) noexcept;
    T* operator++() volatile noexcept;
    T* operator++() noexcept;
    T* operator--() volatile noexcept;
    T* operator--() noexcept;
    T* operator+=(ptrdiff_t) volatile noexcept;
    T* operator+=(ptrdiff_t) noexcept;
    T* operator-=(ptrdiff_t) volatile noexcept;
    T* operator-=(ptrdiff_t) noexcept;
  };
}

Имеется частичная специализацияatomic шаблона класса для указателей. Специализации этой частичной специализации - это структуры стандартного макета. У каждого из них есть тривиальный конструктор по умолчанию и тривиальный деструктор.

Ниже приведены описания только для членов, которые отличаются от основного шаблона.

Следующие операции выполняют арифметические действия с указателями. Соответствие ключа, оператора и вычислений:

Таблица139 - Вычисления атомарных указателей
Key Op Вычисление Key Op Вычисление
add + добавление sub - вычитание

T* fetch_key(ptrdiff_t operand, memory_order order = memory_order_seq_cst) volatile noexcept; T* fetch_key(ptrdiff_t operand, memory_order order = memory_order_seq_cst) noexcept;

Requires: T должен быть типом объекта, иначе программа будет некорректной. [ Note: Арифметика указателей наvoid* указатели функций неправильно сформирована. ]end note

Effects: Атомно заменяет значение, на которое указывает, this результатом вычисления, примененного к значению, на которое указываетthis и данноеoperand. На память влияет значениеorder. Эти операции являются атомарными операциями чтения-изменения-записи ([intro.multithread]).

Returns: Атомарно, значение, на которое указываетthis непосредственно перед эффектами.

Remarks: Результатом может быть неопределенный адрес, но в противном случае операции не имеют неопределенного поведения.

T* operator op=(ptrdiff_t operand) volatile noexcept; T* operator op=(ptrdiff_t operand) noexcept;

Effects: Эквивалентен:return fetch_­key(operand) op operand;

32.6.4 Member operators common to integers and pointers to objects [atomics.types.memop]

T operator++(int) volatile noexcept; T operator++(int) noexcept;

Effects: Эквивалентен:return fetchadd(1);

T operator--(int) volatile noexcept; T operator--(int) noexcept;

Effects: Эквивалентен:return fetchsub(1);

T operator++() volatile noexcept; T operator++() noexcept;

Effects: Эквивалентен:return fetch_­add(1) + 1;

T operator--() volatile noexcept; T operator--() noexcept;

Effects: Эквивалентен:return fetch_­sub(1) - 1;