Есть ли способ использовать заборы, чтобы рассуждать о поведении неатомных операций в C11? В частности, я хотел бы сделать код безопасным в ситуациях, когда определенные поля должны быть int
s для совместимости со старыми интерфейсами, которые могут, скажем, читать и писать структуры данных в файлы или передавать их в качестве аргументов системных вызовов. Поскольку нет требования, чтобы atomic_int
даже был того же размера, что и int
, я не могу использовать atomic_int
.Заборы с неатомкой в C11
Вот минимальный рабочий пример, к сожалению, приводит к непредсказуемому поведению в соответствии с разделом 5.1.2.4 пункта 25, из-за гонки данных на ready
:
#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>
int ready; /* purposely NOT _Atomic */
int value;
void
p1()
{
value = 1;
atomic_thread_fence(memory_order_release);
ready = 1;
}
void
p2(void *_ignored)
{
while (!ready)
;
atomic_thread_fence(memory_order_acquire);
printf("%d\n", value);
}
int
main()
{
thrd_t t;
thrd_create(&t, p2, NULL);
p1();
thrd_join(&t, NULL);
}
Мой конкретный вопрос является ли это возможно исправить приведенный выше код чтобы гарантировать печать 1
без изменения ready
до _Atomic
. (Я мог бы сделать ready
volatile
, но не вижу никакого предложения в спецификации, что это поможет.)
Смежный вопрос является ли это безопасно записать код выше в любом случае, потому что любая машина мой код будет работать на имеет согласованность кеша? Я знаю, что manythings go wrong, когда программы C11 содержат так называемые доброкачественные расы, поэтому я действительно ищу специфику того, что правдоподобный компилятор и архитектура могут сделать с приведенным выше кодом, а не общие предупреждения о гонках данных и неопределенных поведение.
Все операции синхронизации на C11 работают только с атоматикой (или 'mtx_t'). Таким образом, без какого-либо атома это никогда не сработает. После того, как у вас есть атом, которым вы управляете состоянием, вы можете спорить с отношением «до того времени», эффекты которого становятся видимыми в потоке, даже для эффектов на объекты, не являющиеся атомами. ** Но ** сделать ваши вещи «совместимыми» со старыми интерфейсами безнадежно с атомарностью C11. Они не созданы для этого. Если эти старые интерфейсы действуют неатомно, то состояние вашей системы ударяет дыры в любом доказательстве согласованности, которое вы могли бы придумать. –
@JensGustedt Чтобы быть ясным, мой вопрос касался заборов, а не атомизма. Заборы действительно ограничивают порядок операций неатомной памяти. В этом конкретном примере релиз-забор происходит до получения забора, поэтому нет гонки на 'value', только на' ready'. Так, может быть, ответ заключается в использовании 'memory_order_acq_rel' в обоих случаях? – user3188445
Нет, я думаю, вы ошибаетесь в этом. Две ограждения в разных потоках синхронизируются только через атом. Нет другого способа утверждать, что один забор происходит перед другим, не модифицировав атомный объект, модификация которого воспринимается другим. –