Разрешает ли модель памяти C / C ++ атомизацию с разной гранулярностью для одних и тех же байтов?

Предположим, что у меня есть следующий тип:

struct T {
  int32 high;
  int32 low;
};

Определено ли поведение для выполнения атомарного доступа (например, atomic_load , atomic_fetch_add ) по всем x , &x->high и &x->low (при условии U* x )?

Насколько я понимаю, модели памяти C / C ++ определяются с использованием историй по отдельным местоположениям (для размещения слабых архитектур памяти). Если доступы могут пересекать местоположения, означает ли это синхронизацию между местоположениями? Если это так, то я предполагаю, что это будет означать, что истории по существу являются байтными, а доступ к int аналогичен синхронизации между основными 4 (или 8) байтами.

редактировать : пересмотрел пример, чтобы избежать объединения, так как основная часть вопроса касается модели параллелизма.

редактировать : исправлено использование стандартной атомики из stdatomic.h

Всего 2 ответа


Для C11 / C18 (я не могу говорить о C ++) стандартные функции atomic_xxx() в <stdatomic.h> определены только для получения аргументов с квалификацией _Atomic . Поэтому для выполнения atomic_xxx() над полями вашей struct T вам потребуется:

struct T {
  _Atomic int32 high;
  _Atomic int32 low;
} ;

struct T foo, bar ;

и тогда вы сможете сделать (например) atomic_fetch_add(&foo->high, 42) . Но bar = atomic_load(&foo) будет неопределенным.

И наоборот, вы могли бы иметь:

struct T {
  int32 high;
  int32 low;
} ;

_Atomic struct T foo ;
        struct T bar ;

и теперь bar = atomic_load(&foo) определена. Но доступ к любому отдельному полю в foo не определен - независимо от того, является ли он _Atomic .

Исходя из Стандарта, объекты _Atomic xxxx следует рассматривать как совершенно отличные от «обычных» объектов xxxx - они могут иметь разные размеры, представления и выравнивания. Поэтому приведение xxxx к / из _Atomic xxxx не более разумно, чем приведение одной struct к / из другой, другой struct .

Но для gcc и встроенных модулей __atomic_xxx() вы можете делать все, что будет поддерживать процессор. Действительно, для gcc (иначе) стандартный atomic_xxx() будет принимать аргументы, которые не являются квалифицированными типами _Atomic , и отображаются на встроенные модули. clang, с другой стороны, рассматривает передачу не квалифицированного типа _Atomic стандартным функциям как ошибку. ИМХО, это ошибка в gcc <stdatomic.h> .


Загрузка неактивного члена объединения дает неопределенное значение независимо от того, используется или не используется __atomic_load для этой загрузки.


Есть идеи?

10000