Created on 2024-11-29.00:00:00 last changed 3 days ago
[ 2025-02-07; Reflector poll ]
Set priority to 4 after reflector poll. Send to SG1.
"Should the example use `unsigned` so there's no chance of UB due to `int` overflowing?"
"Contrived example showing well-known fact that OOTA prohibition is just hand-waving. N3786 introduced the current wording and made no secret of that. P1217 explored the issue further, as have other SG1 papers. Hard problems. Solutions welcome. Don't think this is the place to focus however."
[ Additional discussion from submitter: ]
I meant, that for a classic spin-lock, the code after the loop won't be executed until the loop exits, and the operation is just a pure load with relaxed memory order during the busy wait. The loop for a spin-lock can have three possible cases:
The compiler cannot assume which case the loop for the spin-lock is. The code after the loop won't be reordered(during the busy waiting with a relaxed memory order); otherwise, implementing the spin-lock would be an issue based on the assumption that the codes after the loop wouldn't be reached when the loop was busy waiting.
In this example, the loop has the same possible cases as the loop in the spin-lock. So, the execution of `#5` seems to depend on the loop and the loop depends on the `pre`. why I mention the spin-lock just is to justify that the code after the loop won't be reordered in both compile-time and runtime even though the condition of the loop is a pure load with a relaxed memory order(otherwise, the foundation for which the busy-waiting of a spink-lock is based on won't be true.). So, the loop has a similar effect to the `if` statement on the control flow: the code in the block of the `if` statement won't be executed if the condition is false, as well, the code after the loop won't be executed if the loop hasn't yet exited.
[atomics.order] p8 and p9 gave two paradigmatic examples of how "circularly depend on their own computation" means. However, consider this example:
std::atomic<int> x = 0, y = 2; // thread 1: if (y.load(relaxed) == 1) { // #1 x.store(1, relaxed); // #2 } //thread 2: int pre = x.load(relaxed); // #3 while (pre != 0) { if (x.compare_exchange_strong(pre, pre + 1, acquire, relaxed)) { // #4 break; } } y.store(1, relaxed); // #5
when both `#1` and `#3` read `1`, is this a kind of OOTA? `#3` depends on `#2`, `#2` depends on `#1`, `#1` depends on `#5`, and the execution of `#5` depends on the exiting of the loop, which in turn initially depends on `pre`.
The loop can never execute, exit after certain iterations, or be a long-time-running without exiting (i.e. `cmpxchg` keeps failing). So, it is unclear whether the execution of `#5` depends on the loop. However, it resembles the `spin-loop` (a failed `cmpxchg` is a pure load with a relaxed load), and the subsequent codes won't execute until the loop exits. So, the scenario of spin-lock seems to agree that the code after a loop depends on the loop(regardless of whether the loop can quickly exit or be a long-time-run loop). From this perspective, the `while` case is something like the `if`, for `if`, the condition is not `true`, and the code thereof cannot be executed. Similarly, a code after a while cannot be executed if the loop doesn't exit. Suggested resolution: Either accurately specify what "circularly depend on their own computation" means, or add a paradigmatic example regarding loop to indicate what it means.History | |||
---|---|---|---|
Date | User | Action | Args |
2025-02-07 20:50:58 | admin | set | messages: + msg14600 |
2025-02-07 20:50:58 | admin | set | messages: + msg14599 |
2025-02-07 20:50:58 | admin | set | status: new -> open |
2024-11-29 00:00:00 | admin | create |