32 Atomic operations library [atomics]

32.1 General [atomics.general]

В этом разделе описаны компоненты для детального атомарного доступа. Этот доступ предоставляется через операции с атомарными объектами.

В следующих подпунктах описываются требования к атомарности и компоненты для типов и операций, как кратко изложено ниже.

Таблица 137 - Сводка библиотеки Atomics
Подпункт Заголовок (ы)
[atomics.order] Порядок и последовательность
[atomics.lockfree] Замок-свободная собственность
[atomics.types.generic] Атомные типы <atomic>
[atomics.types.operations] Операции с атомными типами
[atomics.flag] Тип флага и операции
[atomics.fences] Заборы

32.2 Header <atomic> synopsis [atomics.syn]

namespace std {
  // [atomics.order], order and consistency
  enum memory_order;
  template <class T>
    T kill_dependency(T y) noexcept;

  // [atomics.lockfree], lock-free property
  #define ATOMIC_BOOL_LOCK_FREE unspecified
  #define ATOMIC_CHAR_LOCK_FREE unspecified
  #define ATOMIC_CHAR16_T_LOCK_FREE unspecified
  #define ATOMIC_CHAR32_T_LOCK_FREE unspecified
  #define ATOMIC_WCHAR_T_LOCK_FREE unspecified
  #define ATOMIC_SHORT_LOCK_FREE unspecified
  #define ATOMIC_INT_LOCK_FREE unspecified
  #define ATOMIC_LONG_LOCK_FREE unspecified
  #define ATOMIC_LLONG_LOCK_FREE unspecified
  #define ATOMIC_POINTER_LOCK_FREE unspecified

  // [atomics.types.generic], atomic
  template<class T> struct atomic;
  // [atomics.types.pointer], partial specialization for pointers
  template<class T> struct atomic<T*>;

  // [atomics.nonmembers], non-member functions
  template<class T>
    bool atomic_is_lock_free(const volatile atomic<T>*) noexcept;
  template<class T>
    bool atomic_is_lock_free(const atomic<T>*) noexcept;
  template<class T>
    void atomic_init(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
  template<class T>
    void atomic_init(atomic<T>*, typename atomic<T>::value_type) noexcept;
  template<class T>
    void atomic_store(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
  template<class T>
    void atomic_store(atomic<T>*, typename atomic<T>::value_type) noexcept;
  template<class T>
    void atomic_store_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
                               memory_order) noexcept;
  template<class T>
    void atomic_store_explicit(atomic<T>*, typename atomic<T>::value_type,
                               memory_order) noexcept;
  template<class T>
    T atomic_load(const volatile atomic<T>*) noexcept;
  template<class T>
    T atomic_load(const atomic<T>*) noexcept;
  template<class T>
    T atomic_load_explicit(const volatile atomic<T>*, memory_order) noexcept;
  template<class T>
    T atomic_load_explicit(const atomic<T>*, memory_order) noexcept;
  template<class T>
    T atomic_exchange(volatile atomic<T>*, T) noexcept;
  template<class T>
    T atomic_exchange(atomic<T>*, typename atomic<T>::value_type) noexcept;
  template<class T>
    T atomic_exchange_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
                               memory_order) noexcept;
  template<class T>
    T atomic_exchange_explicit(atomic<T>*, typename atomic<T>::value_type,
                               memory_order) noexcept;
  template<class T>
    bool atomic_compare_exchange_weak(volatile atomic<T>*,
                                      typename atomic<T>::value_type*,
                                      typename atomic<T>::value_type) noexcept;
  template<class T>
    bool atomic_compare_exchange_weak(atomic<T>*,
                                      typename atomic<T>::value_type*,
                                      typename atomic<T>::value_type) noexcept;
  template<class T>
    bool atomic_compare_exchange_strong(volatile atomic<T>*,
                                        typename atomic<T>::value_type*,
                                        typename atomic<T>::value_type) noexcept;
  template<class T>
    bool atomic_compare_exchange_strong(atomic<T>*,
                                        typename atomic<T>::value_type*,
                                        typename atomic<T>::value_type) noexcept;
  template<class T>
    bool atomic_compare_exchange_weak_explicit(volatile atomic<T>*,
                                               typename atomic<T>::value_type*,
                                               typename atomic<T>::value_type,
                                               memory_order, memory_order) noexcept;
  template<class T>
    bool atomic_compare_exchange_weak_explicit(atomic<T>*,
                                               typename atomic<T>::value_type*,
                                               typename atomic<T>::value_type,
                                               memory_order, memory_order) noexcept;
  template<class T>
    bool atomic_compare_exchange_strong_explicit(volatile atomic<T>*,
                                                 typename atomic<T>::value_type*,
                                                 typename atomic<T>::value_type,
                                                 memory_order, memory_order) noexcept;
  template<class T>
    bool atomic_compare_exchange_strong_explicit(atomic<T>*,
                                                 typename atomic<T>::value_type*,
                                                 typename atomic<T>::value_type,
                                                 memory_order, memory_order) noexcept;

  template <class T>
    T atomic_fetch_add(volatile atomic<T>*, typename atomic<T>::difference_type) noexcept;
  template <class T>
    T atomic_fetch_add(atomic<T>*, typename atomic<T>::difference_type) noexcept;
  template <class T>
    T atomic_fetch_add_explicit(volatile atomic<T>*, typename atomic<T>::difference_type,
                                memory_order) noexcept;
  template <class T>
    T atomic_fetch_add_explicit(atomic<T>*, typename atomic<T>::difference_type,
                                memory_order) noexcept;
  template <class T>
    T atomic_fetch_sub(volatile atomic<T>*, typename atomic<T>::difference_type) noexcept;
  template <class T>
    T atomic_fetch_sub(atomic<T>*, typename atomic<T>::difference_type) noexcept;
  template <class T>
    T atomic_fetch_sub_explicit(volatile atomic<T>*, typename atomic<T>::difference_type,
                                memory_order) noexcept;
  template <class T>
    T atomic_fetch_sub_explicit(atomic<T>*, typename atomic<T>::difference_type,
                                memory_order) noexcept;
  template <class T>
    T atomic_fetch_and(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
  template <class T>
    T atomic_fetch_and(atomic<T>*, typename atomic<T>::value_type) noexcept;
  template <class T>
    T atomic_fetch_and_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
                                memory_order) noexcept;
  template <class T>
    T atomic_fetch_and_explicit(atomic<T>*, typename atomic<T>::value_type,
                                memory_order) noexcept;
  template <class T>
    T atomic_fetch_or(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
  template <class T>
    T atomic_fetch_or(atomic<T>*, typename atomic<T>::value_type) noexcept;
  template <class T>
    T atomic_fetch_or_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
                               memory_order) noexcept;
  template <class T>
    T atomic_fetch_or_explicit(atomic<T>*, typename atomic<T>::value_type,
                               memory_order) noexcept;
  template <class T>
    T atomic_fetch_xor(volatile atomic<T>*, typename atomic<T>::value_type) noexcept;
  template <class T>
    T atomic_fetch_xor(atomic<T>*, typename atomic<T>::value_type) noexcept;
  template <class T>
    T atomic_fetch_xor_explicit(volatile atomic<T>*, typename atomic<T>::value_type,
                                memory_order) noexcept;
  template <class T>
    T atomic_fetch_xor_explicit(atomic<T>*, typename atomic<T>::value_type,
                                memory_order) noexcept;

  // [atomics.types.operations], initialization
  #define ATOMIC_VAR_INIT(value) see below

  // [atomics.alias], type aliases
  using atomic_bool           = atomic<bool>;
  using atomic_char           = atomic<char>;
  using atomic_schar          = atomic<signed char>;
  using atomic_uchar          = atomic<unsigned char>;
  using atomic_short          = atomic<short>;
  using atomic_ushort         = atomic<unsigned short>;
  using atomic_int            = atomic<int>;
  using atomic_uint           = atomic<unsigned int>;
  using atomic_long           = atomic<long>;
  using atomic_ulong          = atomic<unsigned long>;
  using atomic_llong          = atomic<long long>;
  using atomic_ullong         = atomic<unsigned long long>;
  using atomic_char16_t       = atomic<char16_t>;
  using atomic_char32_t       = atomic<char32_t>;
  using atomic_wchar_t        = atomic<wchar_t>;

  using atomic_int8_t         = atomic<int8_t>;
  using atomic_uint8_t        = atomic<uint8_t>;
  using atomic_int16_t        = atomic<int16_t>;
  using atomic_uint16_t       = atomic<uint16_t>;
  using atomic_int32_t        = atomic<int32_t>;
  using atomic_uint32_t       = atomic<uint32_t>;
  using atomic_int64_t        = atomic<int64_t>;
  using atomic_uint64_t       = atomic<uint64_t>;

  using atomic_int_least8_t   = atomic<int_least8_t>;
  using atomic_uint_least8_t  = atomic<uint_least8_t>;
  using atomic_int_least16_t  = atomic<int_least16_t>;
  using atomic_uint_least16_t = atomic<uint_least16_t>;
  using atomic_int_least32_t  = atomic<int_least32_t>;
  using atomic_uint_least32_t = atomic<uint_least32_t>;
  using atomic_int_least64_t  = atomic<int_least64_t>;
  using atomic_uint_least64_t = atomic<uint_least64_t>;

  using atomic_int_fast8_t    = atomic<int_fast8_t>;
  using atomic_uint_fast8_t   = atomic<uint_fast8_t>;
  using atomic_int_fast16_t   = atomic<int_fast16_t>;
  using atomic_uint_fast16_t  = atomic<uint_fast16_t>;
  using atomic_int_fast32_t   = atomic<int_fast32_t>;
  using atomic_uint_fast32_t  = atomic<uint_fast32_t>;
  using atomic_int_fast64_t   = atomic<int_fast64_t>;
  using atomic_uint_fast64_t  = atomic<uint_fast64_t>;

  using atomic_intptr_t       = atomic<intptr_t>;
  using atomic_uintptr_t      = atomic<uintptr_t>;
  using atomic_size_t         = atomic<size_t>;
  using atomic_ptrdiff_t      = atomic<ptrdiff_t>;
  using atomic_intmax_t       = atomic<intmax_t>;
  using atomic_uintmax_t      = atomic<uintmax_t>;

  // [atomics.flag], flag type and operations
  struct atomic_flag;
  bool atomic_flag_test_and_set(volatile atomic_flag*) noexcept;
  bool atomic_flag_test_and_set(atomic_flag*) noexcept;
  bool atomic_flag_test_and_set_explicit(volatile atomic_flag*, memory_order) noexcept;
  bool atomic_flag_test_and_set_explicit(atomic_flag*, memory_order) noexcept;
  void atomic_flag_clear(volatile atomic_flag*) noexcept;
  void atomic_flag_clear(atomic_flag*) noexcept;
  void atomic_flag_clear_explicit(volatile atomic_flag*, memory_order) noexcept;
  void atomic_flag_clear_explicit(atomic_flag*, memory_order) noexcept;
  #define ATOMIC_FLAG_INIT see below

  // [atomics.fences], fences
  extern "C" void atomic_thread_fence(memory_order) noexcept;
  extern "C" void atomic_signal_fence(memory_order) noexcept;
}

32.3 Type aliases [atomics.alias]

В псевдонимы типа atomic_­intN_­t, atomic_­uintN_­t, atomic_­intptr_­tи atomic_­uintptr_­t определяются тогда и только тогда intN_­t, uintN_­t, intptr_­tи uintptr_­t определены, соответственно.

32.4 Order and consistency [atomics.order]

namespace std {
  enum memory_order {
    memory_order_relaxed, memory_order_consume, memory_order_acquire,
    memory_order_release, memory_order_acq_rel, memory_order_seq_cst
  };
}

Перечисление memory_­order определяет подробный регулярный (неатомарный) порядок синхронизации памяти, как определено в, [intro.multithread] и может предусматривать порядок операций. Его перечисляемые значения и их значения следующие:

  • memory_­order_­relaxed: никакая операция не заказывает память.

  • memory_­order_­release, memory_­order_­acq_­relи memory_­order_­seq_­cst: операция сохранения выполняет операцию освобождения затронутой области памяти.

  • memory_­order_­consume: операция загрузки выполняет операцию потребления в затронутой области памяти. [ Note: Предпочтение memory_­order_­acquire, которое обеспечивает более сильные гарантии, чем memory_­order_­consume. Реализации сочли невозможным обеспечить производительность лучше, чем у memory_­order_­acquire. Пересмотр спецификации находится на рассмотрении. ] end note

  • memory_­order_­acquire, memory_­order_­acq_­relи memory_­order_­seq_­cst: операция загрузки выполняет операцию получения в затронутой области памяти.

[ Note: Указание атомарных операций memory_­order_­relaxed ослаблено в отношении упорядочения памяти. Реализации должны по-прежнему гарантировать, что любой заданный атомарный доступ к конкретному атомарному объекту будет неделимым по отношению ко всем другим атомарным доступам к этому объекту. ] end note

Атомарная операция, A которая выполняет операцию выпуска над атомарным объектом, M синхронизируется с атомарной операцией, B которая выполняет операцию получения M и берет свое значение из любого побочного эффекта в последовательности выпуска, озаглавленной A.

Должен быть единый общий порядок S для всех memory_­order_­seq_­cst операций, соответствующий порядку «происходит до» и порядкам модификации для всех затронутых местоположений, так что каждая memory_­order_­seq_­cst операция, B которая загружает значение из атомарного объекта, M наблюдает одно из следующих значений:

  • результат последней модификации A из того, M что предшествует B в S, если она существует, или

  • если A существует, результат некоторой модификации M этого не происходит memory_­order_­seq_­cst и не происходило раньше A, или

  • если A не существует, результат некоторой модификации M этого не существует memory_­order_­seq_­cst.

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

Для атомарной операции, B которая считывает значение атомарного объекта M, если есть memory_­order_­seq_­cst забор, X упорядоченный ранее B, то B наблюдает либо последнюю memory_­order_­seq_­cst модификацию M предыдущего X в общем порядке, S либо более позднюю модификацию M в его порядке модификации.

Для атомарных операций A и B для атомарного объекта M, где A модифицирует M и B принимает свое значение, если есть memory_­order_­seq_­cst ограждение X , которое A упорядочено до X и B следует X за ним S, то B наблюдает либо эффекты, A либо последующие модификации M в порядке его модификации.

Для атомарных операций A и B на атомарном объекте M, где A модифицирует M и B принимает свое значение, если есть memory_­order_­seq_­cst ограждения, X и Y такое, что A упорядочено до X, Y упорядочено до Bи X предшествует Y в S, то B наблюдает либо эффекты, A либо более позднюю модификацию M в порядке его модификации .

Для атомных модификаций A и B атомного объекта M, B происходит позже , чем A в порядке модификации , M если:

  • есть memory_­order_­seq_­cst забор X таким образом, что A секвенировали до того X, и X предшествует B в S, или

  • есть memory_­order_­seq_­cst забор Y таким образом, что Y секвенировали до того B, и A предшествует Y в S, или

  • есть memory_­order_­seq_­cst заборы X и Y такие, что A упорядочены до X, Y упорядочены до Bи X предшествуют Y в S.

[ Note: memory_­order_­seq_­cst обеспечивает последовательную согласованность только для программы, которая свободна от гонок данных и использует исключительно memory_­order_­seq_­cst операции. Любое использование более слабого порядка приведет к аннулированию этой гарантии, если не будут приняты особые меры предосторожности. В частности, memory_­order_­seq_­cst заборы обеспечивают полный порядок только самих заборов. Как правило, ограждения не могут использоваться для восстановления последовательной согласованности атомарных операций с более слабыми спецификациями упорядочивания. ] end note

Реализации должны гарантировать, что не вычисляются «неожиданные» значения, которые циклически зависят от их собственных вычислений.

[ Note: Например, x и y изначально равна нулю,

// Thread 1:
r1 = y.load(memory_order_relaxed);
x.store(r1, memory_order_relaxed);
// Thread 2:
r2 = x.load(memory_order_relaxed);
y.store(r2, memory_order_relaxed);

не следует производить r1 == r2 == 42, так как хранение от 42 до y возможно только при хранении в x магазинах 42, которое циклически зависит от магазина для y хранения 42. Обратите внимание, что без этого ограничения такое выполнение возможно. ]end note

[ Note: Рекомендация аналогичным образом запрещает r1 == r2 == 42 в следующем примере с снова x и y снова с нулевым значением:

// Thread 1:
r1 = x.load(memory_order_relaxed);
if (r1 == 42) y.store(42, memory_order_relaxed);
// Thread 2:
r2 = y.load(memory_order_relaxed);
if (r2 == 42) x.store(42, memory_order_relaxed);

end note]

Атомарные операции чтения-изменения-записи всегда должны считывать последнее значение (в порядке модификации), записанное перед записью, связанной с операцией чтения-изменения-записи.

Реализации должны делать атомарные хранилища видимыми для атомарных загрузок в течение разумного промежутка времени.

template <class T> T kill_dependency(T y) noexcept;

Effects: Аргумент не carry a dependency соответствует возвращаемому значению.

Returns: y.

32.5 Lock-free property [atomics.lockfree]

#define ATOMIC_BOOL_LOCK_FREE unspecified
#define ATOMIC_CHAR_LOCK_FREE unspecified
#define ATOMIC_CHAR16_T_LOCK_FREE unspecified
#define ATOMIC_CHAR32_T_LOCK_FREE unspecified
#define ATOMIC_WCHAR_T_LOCK_FREE unspecified
#define ATOMIC_SHORT_LOCK_FREE unspecified
#define ATOMIC_INT_LOCK_FREE unspecified
#define ATOMIC_LONG_LOCK_FREE unspecified
#define ATOMIC_LLONG_LOCK_FREE unspecified
#define ATOMIC_POINTER_LOCK_FREE unspecified

В ATOMIC_­..._­LOCK_­FREE макросы указывают свойство блокировки свободной от соответствующих атомарных типов, с подписанные и неподписанные варианты сгруппированы вместе. Свойства также применяются к соответствующим (частичным) специализациям atomic шаблона. Значение 0 указывает, что типы никогда не освобождаются от блокировки. Значение 1 указывает, что типы иногда не блокируются. Значение 2 указывает, что типы всегда свободны от блокировок.

Функция atomic_­is_­lock_­free указывает, свободен ли объект от блокировки. При выполнении любой данной программы результат запроса без блокировки должен быть согласован для всех указателей одного и того же типа.

Атомарные операции, которые не являются свободными от блокировок, считаются потенциально block ([intro.progress]).

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

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;

32.7 Non-member functions [atomics.nonmembers]

Шаблон функции, не являющейся членом, имя которой совпадает с шаблоном atomic_­f или шаблоном, atomic_­f_­explicit вызывает функцию-член fсо значением первого параметра в качестве выражения объекта и значениями остальных параметров (если есть) в качестве аргументов вызова функции-члена, чтобы. Аргумент для параметра типа atomic<T>​::​value_­type* разыменовывается при передаче в вызов функции-члена. Если такой функции-члена не существует, программа имеет неправильный формат.

template<class T> void atomic_init(volatile atomic<T>* object, typename atomic<T>::value_type desired) noexcept; template<class T> void atomic_init(atomic<T>* object, typename atomic<T>::value_type desired) noexcept;

Effects: Неатомарно инициализируется *object значением desired. Эта функция должна применяться только к объектам, которые были созданы по умолчанию, и только один раз. [ Note: Эта семантика обеспечивает совместимость с C. ] [ Параллельный доступ из другого потока, даже через атомарную операцию, составляет гонку данных. ] end noteNote: end note

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

32.8 Flag type and operations [atomics.flag]

namespace std {
  struct atomic_flag {
    bool test_and_set(memory_order = memory_order_seq_cst) volatile noexcept;
    bool test_and_set(memory_order = memory_order_seq_cst) noexcept;
    void clear(memory_order = memory_order_seq_cst) volatile noexcept;
    void clear(memory_order = memory_order_seq_cst) noexcept;

    atomic_flag() noexcept = default;
    atomic_flag(const atomic_flag&) = delete;
    atomic_flag& operator=(const atomic_flag&) = delete;
    atomic_flag& operator=(const atomic_flag&) volatile = delete;
  };

  bool atomic_flag_test_and_set(volatile atomic_flag*) noexcept;
  bool atomic_flag_test_and_set(atomic_flag*) noexcept;
  bool atomic_flag_test_and_set_explicit(volatile atomic_flag*, memory_order) noexcept;
  bool atomic_flag_test_and_set_explicit(atomic_flag*, memory_order) noexcept;
  void atomic_flag_clear(volatile atomic_flag*) noexcept;
  void atomic_flag_clear(atomic_flag*) noexcept;
  void atomic_flag_clear_explicit(volatile atomic_flag*, memory_order) noexcept;
  void atomic_flag_clear_explicit(atomic_flag*, memory_order) noexcept;

  #define ATOMIC_FLAG_INIT see below
}

atomic_­flag Тип обеспечивает классическую функциональность тест-и-набор. Он имеет два состояния: установленное и очищенное.

Операции с объектом типа atomic_­flag не должны блокироваться. [ Note: Следовательно, операции также должны быть безадресными. ] end note

atomic_­flag Типом является стандартной компоновкой структуры. Он имеет тривиальный конструктор по умолчанию и тривиальный деструктор.

Макрос ATOMIC_­FLAG_­INIT должен быть определен таким образом, чтобы его можно было использовать для инициализации объекта типа atomic_­flag в чистое состояние. Макрос можно использовать в виде:

atomic_flag guard = ATOMIC_FLAG_INIT;

Не указано, можно ли использовать макрос в других контекстах инициализации. Для полного объекта статической продолжительности эта инициализация должна быть статической. Если не инициализирован с помощью ATOMIC_­FLAG_­INIT, не указано,atomic_­flag имеет ли объект начальное состояние set или clear.

bool atomic_flag_test_and_set(volatile atomic_flag* object) noexcept; bool atomic_flag_test_and_set(atomic_flag* object) noexcept; bool atomic_flag_test_and_set_explicit(volatile atomic_flag* object, memory_order order) noexcept; bool atomic_flag_test_and_set_explicit(atomic_flag* object, memory_order order) noexcept; bool atomic_flag::test_and_set(memory_order order = memory_order_seq_cst) volatile noexcept; bool atomic_flag::test_and_set(memory_order order = memory_order_seq_cst) noexcept;

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

Returns: Атомарно стоимость объекта непосредственно перед эффектами.

void atomic_flag_clear(volatile atomic_flag* object) noexcept; void atomic_flag_clear(atomic_flag* object) noexcept; void atomic_flag_clear_explicit(volatile atomic_flag* object, memory_order order) noexcept; void atomic_flag_clear_explicit(atomic_flag* object, memory_order order) noexcept; void atomic_flag::clear(memory_order order = memory_order_seq_cst) volatile noexcept; void atomic_flag::clear(memory_order order = memory_order_seq_cst) noexcept;

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

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

32.9 Fences [atomics.fences]

В этом разделе представлены примитивы синхронизации, называемые fences. Ограничения могут иметь семантику приобретения, семантику выпуска или и то, и другое. Забор с семантикой приобретения называется acquire fence. Забор с семантикой выпуска называется файлом release fence.

Ограничение выпуска A синхронизируется с ограничением получения, B если существуют атомарные операции, X и Yоба они работают с некоторым атомарным объектом M, таким, который A упорядочивается раньше X, X модифицируется M, Y упорядочивается раньше Bи Y считывает значение, записанное X или значение, записанное любым побочным эффектом в Гипотетическая последовательность выпуска X могла бы начаться, если бы это была операция выпуска.

Ограничение выпуска A синхронизируется с атомарной операцией, B которая выполняет операцию получения на атомарном объекте, M если существует такая атомарная операция X , которая A упорядочена раньше X, X изменяет Mи B считывает значение, записанное X или значение, записанное любым побочным эффектом в гипотетической последовательности выпуска. X возглавил бы, если бы это была операция по освобождению.

Атомарная операция , A которая является операцией выпуска на атомном объекте M синхронизируется с приобретает забор , B если существует некоторая атомарная операция X на M таким образом, что X секвенируют до того, B и считывает значение , написанное A или значение , записанное с помощью какого - либо побочного эффекта в последовательности высвобождения во главе A.

extern "C" void atomic_thread_fence(memory_order order) noexcept;

Effects: В зависимости от значения orderэта операция:

  • не имеет эффекта, если order == memory_­order_­relaxed;

  • это приобретаемый забор, если order == memory_­order_­acquire || order == memory_­order_­consume;

  • это ограждение выпуска, если order == memory_­order_­release;

  • одновременно является забором для приобретения и забором для выпуска, если order == memory_­order_­acq_­rel;

  • является последовательным последовательным захватом и освобождением, если order == memory_­order_­seq_­cst.

extern "C" void atomic_signal_fence(memory_order order) noexcept;

Effects: Эквивалентно atomic_­thread_­fence(order), за исключением того, что результирующие ограничения порядка устанавливаются только между потоком и обработчиком сигнала, выполняемым в одном потоке.

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