-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
atomic::Ordering docs provide fewer guarantees than C++20 reference #104814
Comments
See also thread "Better understanding atomics" on URLO. |
Also see PR #378 for the Nomicon for an exhaustive proposal to provide better documentation for atomics. |
Rust does have release sequences, and all other rules in C++20 (except for the ones relating to consume). I think it's intentional that we don't have an exhaustive and stand-alone documentation on the atomic memory model, otherwise we'd be just copying C++ docs. The nomicon is meant to be (more) exhaustive but it's written as a guide not a reference. Although the std doc isn't exhaustive, for the things it does contain, they don't conflict with C++20. For release sequences specifically
This only talks about when an Acquiring load sees a value written by a Releasing store. It says nothing about what does or doesn't happen if the Acquiring load sees a value not written by a Releasing store, so it doesn't exclude release sequences. That said English isn't first-order logic so we shouldn't expect readers to think about vacuous truths in implications all the time... I agree we should explicitly state that there are more guarantees to be found in the C++ reference than the ones documented by us. |
The draft (still not the ISO standard, but useful for reference) is available here.
cppreference does define synchronizes-with: |
Isn't that (an early) working draft of the C++ standard (which you linked) instead of the (publicly available) C++ reference that is referenced by the Rust docs? The latter doesn't seem to be authoritative (i.e. not being the standard), but aims to be a "good specification" (see FAQ). The former, instead, specificially is issued with a warning (see table of contents): It says: "Note: this is an early draft. It's known to be incomplet and incorrekt, and it has lots of bad formatting." In either way, it was very difficult for me to find a defintion for "synchronizes-with", starting from the Rust Maybe this could be improved by editing the en.cppreference.com Wiki. But I still think Rust's docs should either
I don't think that the latter is the case, but given the Rust docs, I cannot be entirely sure as a reader. |
@ibraheemdev: Ooops, I totally missed that. I was sure I had checked it several times, but I guess I got confused and probably only searched for "synchronizes-with" with a minus-hyphen insead of a space. Sorry for the confusion. I re-checked, and the section actually wasn't there when I started writing this issue (or got added at around the same time). It was introduced here. |
Problem description
The documentation in
std::sync::atomic
andstd::sync::atomic::Ordering
(source in Rust 1.65) describe that theOrdering
s are equal to those defined in C++20:In
std::sync::atomic
:In
std::sync::atomic::Ordering
:However, at the same time, the documentation of
Ordering::Release
andOrdering::Acquire
(source in Rust 1.6.5) lists the guarantees made by theseOrdering
s. In particular:These guarantees seem to be less than what the C++20 standard guarantees. Thus the explanation of these orderings differs from the C++20 standard.
Consider the following example, where
a: AtomicUsize
, for example:One thread does:
A second thread does:
Suppose the second thread reads the value 11 written by the
fetch_add
operation in the first thread. As this value was not written by a store operation withRelease
or stronger ordering (but withRelaxed
ordering), there are no guarantees that thread two will see data written before any store in thread one.Looking into the C++20 reference, however, reveals a concept named "Release sequences":
To my understanding, the
fetch_add
in thread one is part of the release sequence headed by thestore
.Looking into the Working Draft N4861 of the C++ Standard (the final revision isn't available for free), we see that on page 1525:
Thus
a.store
in thread one would synchronize with thea.load
in thread two, even though the rules described in Rust's documentation ofstd::sync::atomic::Ordering
do not allow this reasoning; i.e. following solely the rules in Rust's documenation, one would (wrongly) assume that there is no synchronization between those two threads if thread two reads the value of11
written by thefetch_add
operation.Additional issues with accessibility
Overall, it's very difficult for someone who starts with the Rust documentation to get an overview on what the Rust atomics really do. The C++20 standard isn't available for free and
the linked C++ reference (C++20 atomic orderings) uses but does not define the "synchronizes-with" relationship (which is vital for following/understanding the atomic orderings).Intent of the Rust documentation
Before making any fixes to the documentation, it should be clarified whether the difference between the Rust documentation and the C++20 reference exists intentionally, i.e. is Rust deliberately giving less guarantees to the programmer than the C++20 standard does? (Even though still following the C++20 memory model in practice "as of now".) Or is the Rust documentation incomplete here?
Possible improvements
Understanding atomics seems to be very complex, and it might be difficult to fully cover all details in the documentation of
std
. However, the description of theOrdering
s in Rust's documentation should not differ from those given in the C++ reference.Possible solutions could be:
std::sync::atomic::Ordering
are non-exhaustive (not to be confused with thenon_exhaustive
property of the enum itself) and that the C++20 standard is the normative source.The text was updated successfully, but these errors were encountered: