Вопрос или проблема
[intro.races] стр. 16 говорит:
Если вычисление значения A атомного объекта M происходит до вычисления значения B для M, и A получает свое значение из побочного эффекта X на M, то значение, вычисленное B, является либо значением, сохраненным X, либо значением, сохраненным побочным эффектом Y на M, где Y следует за X в порядке модификации M.
Это правило обычно можно интерпретировать, используя следующий пример:
std::atomic<int> x{0};
// поток 1:
x.store(1); // #1
x.store(2); // #2
// поток 2:
x.load(); // #3
x.load(); // #4
Согласно [intro.races] стр. 15, #2
следует за #1
в порядке модификации x
, и если #3
читает 1
, то #4
может быть 1
или 2
в соответствии с [intro.races] стр. 16. В этом случае выделенный “где” пункт накладывает требование: если B не читает X
, то он должен читать какой-то побочный эффект, который следует за X
в порядке модификации.
Тем не менее, рассмотрим следующий пример:
std::atomic<int> x{0};
// поток 1:
x.store(3); // #1
// поток 2:
x.store(4); // #2
// поток 3:
x.load(); // #3
x.load(); // #4
Если #3
прочитал 3
, а #4
прочитал 4
, можем ли мы заключить, что 4
следует за 3
в порядке модификации согласно [intro.races] стр. 16? Насколько я понимаю, текущее wording (т.е. “где” пункт) не означает, что он задает порядок для Y
, другими словами, он не говорит ничего вроде: если B читает Y
, то Y
следует за X
в порядке модификации.
Мне интересно, можно ли использовать правило согласованности чтения для вывода порядка модификации во втором примере.
Ответ или решение
По вопросу о том, может ли правило согласованности чтения-чтения (read-read coherence rule) выводить порядок модификаций на основе читаемого значения, можно сказать следующее.
Начнем с того, что правило согласованности чтения, описанное в [intro.races], гласит, что если вычисление значения A атомарного объекта M предшествует вычислению значения B объекта M, и A получает значение от побочного эффекта X на M, то значение, вычисляемое B, будет либо значением, записанным X, либо значением, записанным побочным эффектом Y на M, где Y следует за X в порядке модификаций M.
Рассмотрим первый пример, который вы привели:
std::atomic<int> x{0};
// поток 1:
x.store(1); // #1
x.store(2); // #2
// поток 2:
x.load(); // #3
x.load(); // #4
В этом случае говорит о том, что #2 действительно следует за #1 в порядке модификаций объекта x. Если #3 читает значение 1, то #4 может читать либо 1, либо 2. И действительно, в соответствии с акцентированной частью правила, если B (в данном случае #4) не считывает значение, записанное X (то есть значение 1), оно должно считывать какое-либо значение, которым является побочный эффект Y, следующее за X в порядке модификаций.
Теперь перейдем ко второму примеру:
std::atomic<int> x{0};
// поток 1:
x.store(3); // #1
// поток 2:
x.store(4); // #2
// поток 3:
x.load(); // #3
x.load(); // #4
Здесь мы имеем два потоковых действия (потоки 1 и 2), оба записи в атомарный объект. Если #3 читает значение 3 и #4 читает значение 4, мы сталкиваемся с вопросом о порядке модификаций. В данном случае, согласно определению, изменение значения 4 в потоке 2 не связано с чтением значения 3 в потоке 3 напрямую через согласованность чтения.
Правило, сформулированное в [intro.races], не предоставляет нам возможность однозначно утверждать, что 4 следует за 3 в порядке модификаций только на основании того, что #3 прочитал 3, а #4 прочитал 4. Чтобы утверждать, что #4 (считывающее значение 4) следует за #3 (считывающим значение 3) в порядке модификаций, необходимо больше информации о том, что произошло между этими нагруженными потоками.
Итак, в данном контексте нельзя сделать вывод о том, что модификации следуют друг за другом просто на основании значений, считанных различными потоками. Порядок истинных модификаций требует явного контроля над доступом к атомарным объектам и, соответственно, задачи анализа должны учитывать не только значения, но и видимые зависимости, возникающие в результате межпотокового взаимодействия.
Таким образом, правило согласованности чтения не может использоваться для вывода порядка модификаций в приведенном вами втором примере, так как оно не однозначно указывает на вопрос, каким образом отдых влияния на порядок чтения значений потоков может быть объединен с побочными эффектами в условиях конкурирующих записей.