Spec-Zone.ru › GCC 15

7.9.1 Встроенные функции атомарных операций с учетом модели памяти

Следующие встроенные функции примерно соответствуют требованиям модели памяти C++11. Все они имеют префикс «__atomic», и большинство из них перегружены, поэтому работают с несколькими типами.

Эти функции предназначены для замены устаревших встроенных функций «__sync». Основное отличие заключается в том, что требуемый порядок памяти передается функциям в качестве параметра. В новом коде всегда следует использовать встроенные функции «__atomic», а не «__sync».

Обратите внимание, что встроенные функции «__atomic» предполагают соответствие программ модели памяти C++11. В частности, предполагается, что в программах отсутствуют гонки данных. Подробные требования см. в стандарте C++11.

Встроенные функции «__atomic» можно использовать с любыми целочисленными скалярными типами или типами указателей размером 1, 2, 4 или 8 байт. Также разрешены 16-байтовые целочисленные типы, если архитектура поддерживает «__int128» (см. 128-битные целые числа).

Для четырех неарифметических функций (загрузка, сохранение, обмен и compare_exchange) также существует универсальная версия. Она работает с типами данных любого вида. Если размер конкретного типа данных это позволяет, используется встроенная функция без блокировок; в противном случае остается внешний вызов, который разрешается во время выполнения. Формат этого внешнего вызова совпадает с обычным, за исключением добавленного первым параметром «size_t», указывающего размер объекта, на который указывает указатель. Размеры всех объектов должны совпадать.

Можно задать 6 различных порядков памяти. Они соответствуют одноименным порядкам памяти C++11; подробные определения см. в стандарте C++11 или на вики GCC, посвященной атомарной синхронизации. Для отдельных архитектур целевые платформы могут также поддерживать дополнительные порядки памяти. Подробности см. в документации целевой платформы.

Атомарная операция может как ограничивать перемещение кода, так и отображаться на аппаратные инструкции для синхронизации потоков (например, барьер). Степень этого воздействия определяется порядками памяти, перечисленными здесь примерно по возрастанию строгости. Описание каждого порядка памяти лишь приблизительно иллюстрирует его эффекты и не является спецификацией; точную семантику см. в модели памяти C++11.

__ATOMIC_RELAXED

Не накладывает ограничений на упорядочивание между потоками.

__ATOMIC_CONSUME

В настоящее время реализован с использованием более строгого порядка памяти __ATOMIC_ACQUIRE из-за недостатка в семантике C++11 для memory_order_consume.

__ATOMIC_ACQUIRE

Создает межпоточное отношение «происходит до» от операции сохранения со семантикой release (или более строгой) к этой операции загрузки acquire. Может препятствовать перемещению кода перед операцией.

__ATOMIC_RELEASE

Создает межпоточное отношение «происходит до» для операций загрузки со семантикой acquire (или более строгой), считывающих данные из этого сохранения release. Может препятствовать перемещению кода после операции.

__ATOMIC_ACQ_REL

Объединяет эффекты обоих порядков: __ATOMIC_ACQUIRE и __ATOMIC_RELEASE.

__ATOMIC_SEQ_CST

Обеспечивает полный порядок для всех остальных операций __ATOMIC_SEQ_CST.

Обратите внимание, что в модели памяти C++11 барьеры (например, «__atomic_thread_fence») действуют в сочетании с другими атомарными операциями над конкретными областями памяти (например, атомарными загрузками); операции над конкретными областями памяти не обязательно влияют на другие операции таким же образом.

Целевым архитектурам рекомендуется предоставлять собственные шаблоны для каждой из встроенных атомарных функций. Если целевой платформой шаблон не предоставлен, используются исходные встроенные атомарные функции «__sync», не учитывающие модель памяти, вместе с необходимыми барьерами синхронизации вокруг них для обеспечения требуемого поведения. В этом случае выполнение подчиняется тем же ограничениям, что и у этих встроенных функций.

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

При реализации шаблонов для этих встроенных функций параметр порядка памяти можно игнорировать, если шаблон реализует наиболее строгий порядок памяти __ATOMIC_SEQ_CST. С любым другим порядком памяти операции будут выполняться корректно и с этим порядком, но могут быть не такими эффективными, как при более подходящей реализации требований к relaxed-порядку.

Обратите внимание, что стандарт C++11 позволяет определять параметр порядка памяти во время выполнения, а не во время компиляции. Эти встроенные функции отображают любое значение, полученное во время выполнения, на __ATOMIC_SEQ_CST, а не вызывают библиотечную процедуру во время выполнения и не встраивают оператор switch. На данный момент это соответствует стандарту, безопасно и является наиболее простым подходом.

Параметр порядка памяти имеет тип signed int, но для порядка памяти зарезервированы только младшие 16 бит. Остальная часть signed int зарезервирована для целевой платформы и должна быть равна 0. Использование предопределенных атомарных значений обеспечивает корректное применение.

Архитектура x86 поддерживает дополнительные модификаторы порядка памяти для обозначения критических секций, в которых используется аппаратное устранение блокировок. Эти модификаторы можно побитово объединять со стандартным порядком памяти для атомарных встроенных функций.

__ATOMIC_HLE_ACQUIRE

Начать устранение блокировок для переменной-блокировки. Порядок памяти должен быть __ATOMIC_ACQUIRE или более строгим.

__ATOMIC_HLE_RELEASE

Завершить устранение блокировок для переменной-блокировки. Порядок памяти должен быть __ATOMIC_RELEASE или более строгим.

Если получение блокировки завершается неудачей, для обеспечения высокой производительности необходимо быстро прервать транзакцию. Это можно сделать с помощью _mm_pause.

#include <immintrin.h> // For _mm_pause

int lockvar;

/* Acquire lock with lock elision */
while (__atomic_exchange_n(&lockvar, 1, __ATOMIC_ACQUIRE|__ATOMIC_HLE_ACQUIRE))
    _mm_pause(); /* Abort failed transaction */
...
/* Free lock with lock elision */
__atomic_store_n(&lockvar, 0, __ATOMIC_RELEASE|__ATOMIC_HLE_RELEASE);
Встроенная функция: type __atomic_load_n (type *ptr, int memorder)

Эта встроенная функция реализует атомарную операцию загрузки. Она возвращает содержимое *ptr.

Допустимые варианты порядка памяти: __ATOMIC_RELAXED, __ATOMIC_SEQ_CST, __ATOMIC_ACQUIRE и __ATOMIC_CONSUME.

Встроенная функция: void __atomic_load (type *ptr, type *ret, int memorder)

Это универсальная версия атомарной загрузки. Она возвращает содержимое *ptr в *ret.

Встроенная функция: void __atomic_store_n (type *ptr, type val, int memorder)

Эта встроенная функция реализует атомарную операцию сохранения. Она записывает val в *ptr.

Допустимые варианты порядка памяти: __ATOMIC_RELAXED, __ATOMIC_SEQ_CST и __ATOMIC_RELEASE.

Встроенная функция: void __atomic_store (type *ptr, type *val, int memorder)

Это универсальная версия атомарного сохранения. Она сохраняет значение *val в *ptr.

Встроенная функция: type __atomic_exchange_n (type *ptr, type val, int memorder)

Эта встроенная функция реализует атомарную операцию обмена. Она записывает val в *ptr и возвращает предыдущее содержимое *ptr.

Допустимы все варианты порядка памяти.

Встроенная функция: void __atomic_exchange (type *ptr, type *val, type *ret, int memorder)

Это универсальная версия атомарного обмена. Она сохраняет содержимое *val в *ptr. Исходное значение *ptr копируется в *ret.

Встроенная функция: bool __atomic_compare_exchange_n (type *ptr, type *expected, type desired, bool weak, int success_memorder, int failure_memorder)

Эта встроенная функция реализует атомарную операцию сравнения и обмена. Она сравнивает содержимое *ptr с содержимым *expected. Если значения равны, операция является операцией чтения-изменения-записи, которая записывает desired в *ptr. Если значения не равны, операция является операцией чтения, а текущее содержимое *ptr записывается в *expected. Значение weak равно true для слабого варианта compare_exchange, который может завершиться ложной неудачей, и false для строгого варианта, который никогда не завершается ложной неудачей. Многие целевые платформы поддерживают только строгий вариант и игнорируют этот параметр. Если вы сомневаетесь, используйте строгий вариант.

Если desired записывается в *ptr, возвращается true, а на память оказывается влияние в соответствии с порядком памяти, указанным параметром success_memorder. Ограничений на используемый здесь порядок памяти нет.

В противном случае возвращается false, а на память оказывается влияние в соответствии с параметром failure_memorder. Этот порядок памяти не может быть __ATOMIC_RELEASE или __ATOMIC_ACQ_REL. Он также не может быть строже порядка, заданного параметром success_memorder.

Встроенная функция: bool __atomic_compare_exchange (type *ptr, type *expected, type *desired, bool weak, int success_memorder, int failure_memorder)

Эта встроенная функция реализует универсальную версию __atomic_compare_exchange. Функция практически идентична __atomic_compare_exchange_n, за исключением того, что желаемое значение также является указателем.

Встроенная функция: type __atomic_add_fetch (type *ptr, type val, int memorder)
Встроенная функция: type __atomic_sub_fetch (type *ptr, type val, int memorder)
Встроенная функция: type __atomic_and_fetch (type *ptr, type val, int memorder)
Встроенная функция: type __atomic_xor_fetch (type *ptr, type val, int memorder)
Встроенная функция: type __atomic_or_fetch (type *ptr, type val, int memorder)
Встроенная функция: type __atomic_nand_fetch (type *ptr, type val, int memorder)

Эти встроенные функции выполняют операцию, указанную в названии, и возвращают результат операции. Операции с указателями выполняются так, как если бы операнды имели тип uintptr_t. То есть они не масштабируются по размеру типа, на который указывает указатель.

{ *ptr op= val; return *ptr; }
{ *ptr = ~(*ptr & val); return *ptr; } // nand

Объект, на который указывает первый аргумент, должен иметь целочисленный тип или тип указателя. Он не должен иметь тип boolean. Допустимы все порядки памяти.

Встроенная функция: type __atomic_fetch_add (type *ptr, type val, int memorder)
Встроенная функция: type __atomic_fetch_sub (type *ptr, type val, int memorder)
Встроенная функция: type __atomic_fetch_and (type *ptr, type val, int memorder)
Встроенная функция: type __atomic_fetch_xor (type *ptr, type val, int memorder)
Встроенная функция: type __atomic_fetch_or (type *ptr, type val, int memorder)
Встроенная функция: type __atomic_fetch_nand (type *ptr, type val, int memorder)

Эти встроенные функции выполняют операцию, указанную в названии, и возвращают значение, которое до этого содержалось в *ptr. Операции с указателями выполняются так, как если бы операнды имели тип uintptr_t. То есть они не масштабируются по размеру типа, на который указывает указатель.

{ tmp = *ptr; *ptr op= val; return tmp; }
{ tmp = *ptr; *ptr = ~(*ptr & val); return tmp; } // nand

К аргументам применяются те же ограничения, что и к соответствующим встроенным функциям __atomic_op_fetch. Допустимы все порядки памяти.

Встроенная функция: bool __atomic_test_and_set (void *ptr, int memorder)

Эта встроенная функция выполняет атомарную операцию test-and-set над байтом по адресу *ptr. Байту присваивается некоторое ненулевое значение «установлено», определяемое реализацией; возвращаемое значение равно true тогда и только тогда, когда предыдущее содержимое было «установлено». Ее следует использовать только для операндов типа bool или char. Для других типов может быть установлен лишь фрагмент значения.

Допустимы все порядки памяти.

Встроенная функция: void __atomic_clear (bool *ptr, int memorder)

Эта встроенная функция выполняет атомарную операцию очистки над *ptr. После операции *ptr содержит 0. Ее следует использовать только для операндов типа bool или char и совместно с __atomic_test_and_set. Для других типов может быть выполнена лишь частичная очистка. Если тип не является bool, предпочтительно использовать __atomic_store.

Допустимые варианты порядка памяти: __ATOMIC_RELAXED, __ATOMIC_SEQ_CST и __ATOMIC_RELEASE.

Встроенная функция: void __atomic_thread_fence (int memorder)

Эта встроенная функция действует как барьер синхронизации между потоками в соответствии с указанным порядком памяти.

Допустимы все порядки памяти.

Встроенная функция: void __atomic_signal_fence (int memorder)

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

Допустимы все порядки памяти.

Встроенная функция: bool __atomic_always_lock_free (size_t size, void *ptr)

Эта встроенная функция возвращает true, если объекты размером size байт всегда порождают атомарные инструкции без блокировок для целевой архитектуры. Значение size должно быть константой времени компиляции, и результат также вычисляется как константа времени компиляции.

ptr — необязательный указатель на объект, который может использоваться для определения выравнивания. Значение 0 означает, что следует использовать обычное выравнивание. Компилятор также может игнорировать этот параметр.

if (__atomic_always_lock_free (sizeof (long long), 0))
Встроенная функция: bool __atomic_is_lock_free (size_t size, void *ptr)

Эта встроенная функция возвращает true, если объекты размером size байт всегда порождают атомарные инструкции без блокировок для целевой архитектуры. Если неизвестно, что встроенная функция не использует блокировки, выполняется вызов процедуры времени выполнения с именем __atomic_is_lock_free.

ptr — необязательный указатель на объект, который может использоваться для определения выравнивания. Значение 0 означает, что следует использовать обычное выравнивание. Компилятор также может игнорировать этот параметр.

© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-15.3.0/gcc/_005f_005fatomic-Builtins.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API