Постановление стандарта C11 заключается в следующем.
5.1.2.4 Многопоточные выполнения и гонки данных
Оценка A упорядочивается по зависимостям раньше 16) оценки B, если:
— A выполняет операцию выпуска для атомарного объекта M, а в другом потоке B выполняет операцию потребления для M и считывает значение, записанное любым побочным эффектом в последовательности выпуска, возглавляемой A, или
- для некоторой оценки X, A упорядочен по зависимостям до того, как X и X перенесут зависимость к B.
Оценка A inter-thread выполняется перед оценкой 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 удовлетворяет условиям:
— А происходит раньше Б, и
- нет другого побочного эффекта от X к M, такого, что A происходит раньше X, а X происходит раньше B.
Значение неатомарного скалярного объекта M, определенное оценкой B, должно быть значением, сохраненным видимым побочным эффектом A.
(выделение добавлено)
В комментарии ниже я буду сокращать нижеследующее:
- По порядку зависимостей до: дата рождения
- Переход между потоками происходит раньше: 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)
}
Чтобы доказать, что утверждение никогда не срабатывает, мы пытаемся доказать, что 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) не выполняется. Утверждение может завершиться ошибкой.
Как это может происходить, описано здесь, в обсуждении модели памяти стандарта C++ и здесь, Раздел 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 перед C, заключается в том, что consume не запрещает это. Это отличается от acquire-load, который предотвращает изменение порядка загрузки/сохранения после в программном порядке перед. Опять же, в 5.1.2.4(15) указано, что операция потребления, участвующая в отношении «предшествующий порядок зависимостей», обеспечивает упорядочение только в отношении операций, от которых эта операция потребления действительно несет зависимость, и совершенно определенно нет зависимости между двумя нагрузками.
Проверка CppMem
CppMem – это инструмент, помогающий исследовать сценарии доступа к общим данным. в моделях памяти C11 и C++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
.
person
Iwillnotexist Idonotexist
schedule
17.08.2015
return b
в первом блоке кода в кавычках. Сразу после указанного блока кода он говорит: Приведенный выше пример может иметь возвращаемое значение 0, если напрямую перевести его в ARM или Power, верно? Оба они могут преобразовывать чтение в условное выражение, и оба могут преобразовывать потребляющую нагрузку в простую загрузку, если зависимости данных остаются неразрывными. - person Iwillnotexist Idonotexist   schedule 09.09.2015return b
должен загружатьb
неатомарно (поэтому предлагаемая альтернатива использования приобретения имеет смысл). Йенс Густедт утверждает, что существует гарантированный порядок между двумя атомарными потребительскими нагрузками. - person dyp   schedule 09.09.2015