Есть ли способ использовать заборы, чтобы рассуждать о поведении неатомарных операций в C11? В частности, я хотел бы сделать код безопасным в ситуациях, когда некоторые поля должны быть int
для совместимости со старыми интерфейсами, которые могут, скажем, читать и записывать структуры данных в файлы или передавать их в качестве аргументов системного вызова. Поскольку нет требования, чтобы atomic_int
был хотя бы того же размера, что и int
, я не могу использовать atomic_int
.
Вот минимальный рабочий пример, который, к сожалению, приводит к неопределенному поведению в соответствии с параграфом 25 раздела 5.1.2.4 из-за гонки данных на 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
, но не вижу в спецификации никаких указаний на то, что это поможет.)
Связанный с этим вопрос заключается в том, безопасно ли в любом случае писать приведенный выше код, потому что любая машина, на которой будет выполняться мой код, имеет когерентность кеша? Мне известно, что многие вещи идут неправильно, когда программы C11 содержат так называемые доброкачественные расы, поэтому я действительно ищу специфику того, что правдоподобный компилятор и архитектура могут сделать с приведенным выше кодом, а не общие предупреждения о гонках данных и неопределенном поведении.
mtx_t
). Так что без атома это никогда не сработает. Когда у вас есть атом, состоянием которого вы управляете, вы можете спорить с отношением «происходит до того», какие эффекты становятся видимыми в каком потоке, даже для эффектов на объектах, не являющихся атомами. Но сделать ваши вещи совместимыми со старыми интерфейсами безнадежно с C11 atomics. Они не созданы для этого. Разрешение этим старым интерфейсам действовать неатомарно в отношении состояния вашей системы пробивает дыры в любом доказательстве непротиворечивости, которое вы можете придумать. - person Jens Gustedt   schedule 09.02.2017value
, только наready
. Так что, возможно, ответ заключается в том, чтобы использоватьmemory_order_acq_rel
в обоих случаях? - person user3188445   schedule 10.02.2017volatile
для всего, что вообще должно соблюдать какую-либо последовательность. - person supercat   schedule 10.02.2017atomic_int
, еслиsizeof(atomic_int) == sizeof(int)
, а в противном случае использовать volatile (полагая, что вы используете какую-то архаичную одноядерную архитектуру)? Можно ли использовать междуint *
иatomic_int *
, если они одного размера? - person user3188445   schedule 11.02.2017