Не пойму определение strongly happens before, или это ошибка в стандарте С++20 (проекте C++23)?

6.9.2.1 Data races

11 An evaluation A simply happens before an evaluation B if either
(11.1) — A is sequenced before B, or
(11.2) — A synchronizes with B, or
(11.3) — A simply happens before X and X simply happens before B.
[Note: In the absence of consume operations, the happens before and simply happens before relations are identical. —end note]

12 An evaluation A strongly happens before an evaluation D if, either
(12.1) — A is sequenced before D, or
(12.2) — A synchronizes with D, and both A and D are sequentially consistent atomic operations (31.4), or
(12.3) — there are evaluations B and C such that A is sequenced before B, B simply happens before C, and C is sequenced before D, or
(12.4) — there is an evaluation B such that A strongly happens before B, and B strongly happens before D.
[Note: Informally, if A strongly happens before B, then A appears to be evaluated before B in all contexts. Strongly happens before excludes consume operations. —end note]

Вряд ли же, к примеру варианта 12.3, в случае потока 1:
t1 = A; atm.store(t1, model_order_release) /*B, B “синхронизируеся-с” C, т.е. B “происходит-просто-до” C */;
И потока 2:
t2 = atm.load(model_order_acquire) /*C*/; D = t2;

Вычисление A будет видно ранее вычисления D во всех контекстах, т.к. вариант 12.3 целиком это же тоже "происходит-просто-до". Или я что-то упускаю и/или не понимаю?

А если это ошибка, то какое определение “происходит-строго-до” могло бы иметься ввиду? Так:

12 An evaluation A strongly happens before an evaluation B if, either
(12.1) — удалён
(12.2) — A synchronizes with B, and both A and B are sequentially consistent atomic operations (31.4), or
(12.x) — A strongly happens before X, and X simply happens before B, or
(12.x) — A simply happens before X, and X strongly happens before B, or

(12.x) — A strongly happens before X, and X strongly happens before B.
[Note: Informally, if A strongly happens before B, then A appears to be evaluated before B in all contexts. Strongly happens before excludes consume operations. —end note]

Или так:

12 An evaluation A strongly happens before an evaluation B if, either
(12.1) — A is sequenced before B, or
(12.2) — A synchronizes with B, and both A and B are sequentially consistent atomic operations (31.4), or
(12.x) — A strongly happens before X, and X simply happens before B, or
(12.x) — A simply happens before X, and X strongly happens before B, or

(12.x) — A strongly happens before X, and X strongly happens before B.
[Note: Informally, if A strongly happens before B (A and B in different threads), then A appears to be evaluated before B in all contexts. Strongly happens before excludes consume operations. —end note]

Но наверное всё-таки вот так:

12 An evaluation A strongly happens before an evaluation B if, either
(12.1) — for some sequentially consistent acquire and release fence F (31.11)
(12.1.1)
— A is sequenced before F, and F sequenced before B, or
(12.2) — A synchronizes with B, and both A and B are sequentially consistent atomic operations (31.4), or
(12.3) — for some evaluation X
(12.3.1) — A simply happens before X, and X strongly happens before B, or
(12.3.2) — A strongly happens before X, and X simply happens before B, or
(12.3.3)
— A strongly happens before X, and X strongly happens before B.
[Note: For single threaded applications, an implementation may use atomic_signal_fence instead of atomic_thread_fence if there is signal processing, and eliminate it if there is no signal processing. —end note]
[Note: Informally, if A strongly happens before B, then A appears to be evaluated before B in all contexts. Strongly happens before excludes consume operations. —end note]

P.S.
Отношение “происходит-строго-до” (этот вариант определения отношения введён в C++20, а то что в C++17 было отношением “происходит-строго-до” в C++20 стало называться отношением “происходит-просто-до”; да, в C++14/C++11 вообще было только "происходит-до"), как указано в примечании, и по смыслу его использования в остальном тексте стандарта C++20/C++23, описывает последовательное согласование видимых побочных эффектов для любых потоков и контекстов (memory_order_seq_cst). Но вариант 12.3, по фактическому тексту, является всего лишь "происходит-просто-до" длины 3 или более, которое описывает согласование видимых побочных эффектов для двух потоков и не даёт, в общем случае, никаких гарантий для остальных потоков

P.P.S.
Вроде как, ошибка во внутренней противоречивости самого текущего определения “происходит-строго-до”, утв. 12.3 противоречит утв. 12.2 и примечанию. Но, возможно, интересно использование отношения “происходит-строго-до” в тексте C++20/23:

  • Отделение статической инициализации (6.9.3.2 [basic.start.static]) от динамической инициализации (6.9.3.3 [basic.start.dynamic]), и их обеих от main() основной программы и/или от любой другой не inline функции модулей трансляции;
  • Отделение деструкторов локальной-памяти-потоков от статических деструкторов, от конструкторов, а так же от зарегистрированных std::atexit() обработчиков и этих обработчиков друг от друга (6.9.3.4 [basic.start.term]);
  • Формальное описание связи единого общего порядка последовательно согласованных атомарных переменных и отношения “происходит-строго-до” (31.4 [atomics.order]);
  • Операции освобождения (выпуска) семафоров (32.7.3 [thread.sema.cnt]), защёлок (32.8.2.3 [thread.latch.class]) и барьеров (32.8.3.3 [thread.barrier.class]), на мой взгляд, неоправданно объявлены как “происходящие-строго-до” операций захвата (получения). В принципе, атомарные переменные тоже, по умолчанию memory_order_seq_cst, но у атомарных переменных это можно изменить, а здесь такой возможности нет. 
Тем не менее, смысл может быть в том, что по POSIX все вызовы всех функций синхронизации должны быть последовательно согласованы. Конечно, ради эффективности, современные реализации этого не придерживаются, но, вроде как, только в части мьютексов.

Ответы (0 шт):