Ответ 1
Правило C11 Standard выглядит следующим образом.
5.1.2.4 Многопоточные исполнения и расписания данных
Оценка A является зависимой от заказа до 16) оценкой B, если:
- A выполняет операцию освобождения на атомарном объекте M, а в другом потоке B выполняет операцию потребления на M и считывает значение, записанное любым побочным эффектом в последовательности выпуска, возглавляемой A, или
- для некоторой оценки X, A является зависимым от нормы до X и X несет зависимость от B.
Оценка. Межпотоковая передача происходит до оценки B, если A синхронизируется с B, A является упорядоченной зависимостью до B или для некоторой оценки X:
- A синхронизируется с X и X секвенируется до B,
- A секвенирован до того, как X и X межпоточность произойдет до B, или
- Между потоками происходит до того, как X и X межпоточные события произойдут до B.
ПРИМЕЧАНИЕ 7 "Интер-поток" происходит до того, как отношение описывает произвольные конкатенации '', секвенированные ранее, '' синхронизирует с и '' зависимость, упорядоченную до отношений, с двумя исключениями, Первое исключение состоит в том, что конкатенация не разрешается заканчивать "упорядоченной по заказу", а затем "секвентированной ранее". Причина этого ограничения заключается в том, что операция потребления, участвующая в "зависимой от зависимостей до отношения", обеспечивает упорядочение только по отношению к операциям, к которым эта операция потребления фактически несет зависимость. Причина, по которой это ограничение применяется только к конец такой конкатенации состоит в том, что любая последующая операция освобождения обеспечит требуемый порядок для предварительной обработки. Второе исключение состоит в том, что конкатенация не разрешается полностью состоять из "секвенированных ранее". Причины этого ограничения заключаются в следующем: (1) разрешить "межпоточность" до того, как быть транзитивно закрытой, и (2) "происходит до того, как отношение, определенное ниже, предусматривает отношения, состоящие целиком из" секвенированных ранее ".
Оценка A происходит перед оценкой B, если A секвенирован до B или. Межпоточная передача происходит до B.
Видимый побочный эффект A на объекте M относительно вычисления значений B из M удовлетворяет условиям:
- A происходит до B и
- нет другого побочного эффекта от X до M, такого, что A происходит до того, как X и X произойдет до B.
Значение неатомного скалярного объекта M, как определено оценкой B, должно быть значением, сохраненным видимым побочным эффектом A.
(выделено курсивом)
В комментарии ниже я сокращу ниже:
- Зависимость, упорядоченная до: DOB
- Интер-поток происходит до: ITHB
- Бывает раньше: HB
- Последовательность перед: SeqB
Давайте рассмотрим, как это применимо. У нас есть 4 важных операции памяти, которые мы назовем оценками A, B, C и D:
Тема 1:
y.store (20); // Release; Evaluation A
x.store (10); // Release; Evaluation B
Тема 2:
if (x.load() == 10) { // Consume; Evaluation C
assert (y.load() == 20) // Consume; Evaluation D
y.store (10)
}
Чтобы доказать, что assert никогда не срабатывает, мы фактически пытаемся доказать, что A всегда является видимым побочным эффектом в D. В соответствии с 5.1.2.4 (15) имеем:
A SeqB B DOB C SeqB D
который является конкатенацией, заканчивающейся DOB, а затем SeqB. Это явно управляется (17), чтобы не быть конкатенацией ITHB, несмотря на то, что говорит (16).
Мы знаем, что поскольку A и D не находятся в одном и том же потоке выполнения, A не является SeqB D; Следовательно, ни одно из двух условий в (18) для HB не выполняется, а A не HB D.
Отсюда следует, что A не виден D, так как одно из условий (19) не выполняется. Утверждение может потерпеть неудачу.
Как это могло бы произойти, тогда описывается здесь, в обсуждении модели стандартной модели С++ и здесь, раздел 4.2 "Зависимости управления" :
- (Некоторое время вперед) Прогнозирование ветвления 2-го канала предполагает, что будет выполняться
if
. - Тема 2 приближается к предсказанной ветки и начинает спекулятивную выборку.
- Резьба 2 вне порядка и спекулятивно загружает
0xGUNK
изy
(Оценка D). (Возможно, он еще не был выведен из кеша?). - Тема 1 хранит
20
вy
(оценка A) - Тема 1 хранит
10
вx
(оценка B) - Тема 2 загружает
10
изx
(Оценка C) - Тема 2 подтверждает, что выполняется
if
. - Выполняется 2-я спекулятивная нагрузка
y == 0xGUNK
. - Ошибка потока 2.
Причина, по которой для оценки D допускается переупорядочивать до С, потому что потребление не запрещает ее. Это отличается от нагрузки на загрузку, которая предотвращает перегрузку любой загрузки/сохранения после того, как она была в порядке выполнения программы. Опять же, 5.1.2.4 (15) утверждает, что операция потребления, участвующая в "зависимой от зависимостей до отношений", обеспечивает упорядочение только по отношению к операциям, к которым эта операция потребления фактически несет зависимость, и там определенно не является зависимость между две нагрузки.
Проверка CppMem
CppMem - это инструмент, который помогает исследовать общие сценарии доступа к данным в моделях памяти C11 и С++ 11.
Для следующего кода, который аппроксимирует сценарий в вопросе:
int main() {
atomic_int x, y;
y.store(30, mo_seq_cst);
{{{ { y.store(20, mo_release);
x.store(10, mo_release); }
||| { r3 = x.load(mo_consume).readsvalue(10);
r4 = y.load(mo_consume); }
}}};
return 0; }
Инструмент сообщает о двух последовательных сценариях без гонок, а именно:
В котором y=20
успешно читается, и
В котором считывается "устаревшее" значение инициализации y=30
. Свободный круг - мой.
В отличие от этого, когда mo_acquire
используется для нагрузок, CppMem сообщает только о непротиворечивом сценарии один, а именно о правильном:
в котором читается y=20
.