Date: Mon, 18 Jul 2022 20:54:34 +0200
pon., 18 lip 2022 o 20:29 Andrey Semashev via Std-Discussion
<std-discussion_at_[hidden]> napisaĆ(a):
>
> On 7/18/22 20:40, Nate Eldredge wrote:
> > On Mon, 18 Jul 2022, Andrey Semashev via Std-Discussion wrote:
> >
> >> On 7/18/22 17:48, Nate Eldredge via Std-Discussion wrote:
> >
> >>> Suppose that `b.wait(false)` starts while the value of `b` is still
> >>> `false`, and so it blocks. When `b.notify_one()` executes, the main
> >>> thread will unblock and test the value of `b`. However, I cannot find
> >>> anything to clearly imply that the store of `true` to `b` will be
> >>> visible by then. If the main thread loads the old value `false`, it
> >>> will block again, and potentially never wake up (unless there is a
> >>> spurious unblock).
> >>
> >> What you're saying is only possible if notify_one is observed before the
> >> store by the main thread. And that is not possible as the store is
> >> sequenced-before the notify. Consequently, the store happens-before the
> >> notify:
> >>
> >> http://eel.is/c++draft/intro.multithread#intro.races-10
> >
> > I agree that the store happens-before notify_one() by sequencing, but I
> > don't see how that helps by itself. What we need is for the store in
> > thr1 to happen-before the load inside b.wait() in the main thread.
> > Since these are in separate threads, sequencing alone cannot get us that
> > relation.
>
> If the store is not observed by the load then wait blocks until
> notify_one. Note that this load-test-and-block is atomic. Then the
> notify_one will unblock the wait when it happens.
>
> If you're asking whether the standard guarantees that loads observe the
> stores then this is guaranteed here:
>
> http://eel.is/c++draft/atomics.order#3
> http://eel.is/c++draft/atomics.order#10
> http://eel.is/c++draft/atomics.order#11
>
> Also, the unblocking is described here:
>
> http://eel.is/c++draft/atomics.wait#4
>
> > By my reading of [data.races p7-12], if we disregard consume operations,
> > the only way to get a happens-before between two evaluations in
> > different threads is to have a synchronizes-with somewhere in the
> > chain. The example program doesn't have any synchronizes-with relations
> > that I can find, so I don't see how we can ever establish the thr1 store
> > to happen-before the main thread load.
>
> Synchronizes-with is not the only relation between events in different
> threads. As shown above, if the store is coherence-ordered before the
> load then the load will observe its effect. Since the store and notify
> effects are observed in that order, and wait is atomic, there is no way
> that wait does not observe the store and yet is woken by the notify.
To compare to `atomics.wait#4` we have:
```
std::atomic<bool> b{false}; //EVENT X
void thr1() {
b.store(true, std::memory_order_relaxed); //EVENT Y
b.notify_one(); //NOTIFY
}
int main() {
std::thread t(thr1);
b.wait(false); //BLOCK
t.join();
return 0;
}
```
"the atomic waiting operation has blocked after observing the result
of X," -> this X happens before `main` start, and means should be
visible for block operation.
"X precedes Y in the modification order of M, and" -> again X happens
before thread start
"Y happens before the call to the atomic notifying operation." -> is
line before notify
For mea this is clear that the function should unblock there.
Is This correct interpretation?
<std-discussion_at_[hidden]> napisaĆ(a):
>
> On 7/18/22 20:40, Nate Eldredge wrote:
> > On Mon, 18 Jul 2022, Andrey Semashev via Std-Discussion wrote:
> >
> >> On 7/18/22 17:48, Nate Eldredge via Std-Discussion wrote:
> >
> >>> Suppose that `b.wait(false)` starts while the value of `b` is still
> >>> `false`, and so it blocks. When `b.notify_one()` executes, the main
> >>> thread will unblock and test the value of `b`. However, I cannot find
> >>> anything to clearly imply that the store of `true` to `b` will be
> >>> visible by then. If the main thread loads the old value `false`, it
> >>> will block again, and potentially never wake up (unless there is a
> >>> spurious unblock).
> >>
> >> What you're saying is only possible if notify_one is observed before the
> >> store by the main thread. And that is not possible as the store is
> >> sequenced-before the notify. Consequently, the store happens-before the
> >> notify:
> >>
> >> http://eel.is/c++draft/intro.multithread#intro.races-10
> >
> > I agree that the store happens-before notify_one() by sequencing, but I
> > don't see how that helps by itself. What we need is for the store in
> > thr1 to happen-before the load inside b.wait() in the main thread.
> > Since these are in separate threads, sequencing alone cannot get us that
> > relation.
>
> If the store is not observed by the load then wait blocks until
> notify_one. Note that this load-test-and-block is atomic. Then the
> notify_one will unblock the wait when it happens.
>
> If you're asking whether the standard guarantees that loads observe the
> stores then this is guaranteed here:
>
> http://eel.is/c++draft/atomics.order#3
> http://eel.is/c++draft/atomics.order#10
> http://eel.is/c++draft/atomics.order#11
>
> Also, the unblocking is described here:
>
> http://eel.is/c++draft/atomics.wait#4
>
> > By my reading of [data.races p7-12], if we disregard consume operations,
> > the only way to get a happens-before between two evaluations in
> > different threads is to have a synchronizes-with somewhere in the
> > chain. The example program doesn't have any synchronizes-with relations
> > that I can find, so I don't see how we can ever establish the thr1 store
> > to happen-before the main thread load.
>
> Synchronizes-with is not the only relation between events in different
> threads. As shown above, if the store is coherence-ordered before the
> load then the load will observe its effect. Since the store and notify
> effects are observed in that order, and wait is atomic, there is no way
> that wait does not observe the store and yet is woken by the notify.
To compare to `atomics.wait#4` we have:
```
std::atomic<bool> b{false}; //EVENT X
void thr1() {
b.store(true, std::memory_order_relaxed); //EVENT Y
b.notify_one(); //NOTIFY
}
int main() {
std::thread t(thr1);
b.wait(false); //BLOCK
t.join();
return 0;
}
```
"the atomic waiting operation has blocked after observing the result
of X," -> this X happens before `main` start, and means should be
visible for block operation.
"X precedes Y in the modification order of M, and" -> again X happens
before thread start
"Y happens before the call to the atomic notifying operation." -> is
line before notify
For mea this is clear that the function should unblock there.
Is This correct interpretation?
Received on 2022-07-18 18:54:46