Skip to content
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

Relax SeqCst ordering in standard library. #122729

Merged
merged 16 commits into from
Mar 21, 2024
Merged

Relax SeqCst ordering in standard library. #122729

merged 16 commits into from
Mar 21, 2024

Conversation

m-ou-se
Copy link
Member

@m-ou-se m-ou-se commented Mar 19, 2024

Every single SeqCst in the standard library is unnecessary. In all cases, Relaxed or Release+Acquire was sufficient.

As I wrote in my book on atomics:

[..] when reading code, SeqCst basically tells the reader: "this operation depends on the total order of every single SeqCst operation in the program," which is an incredibly far-reaching claim. The same code would likely be easier to review and verify if it used weaker memory ordering instead, if possible. For example, Release effectively tells the reader: "this relates to an acquire operation on the same variable," which involves far fewer considerations when forming an understanding of the code.

It is advisable to see SeqCst as a warning sign. Seeing it in the wild often means that either something complicated is going on, or simply that the author did not take the time to analyze their memory ordering related assumptions, both of which are reasons for extra scrutiny.

r? @Amanieu @joboet

m-ou-se added 12 commits March 19, 2024 15:27
SeqCst is unnecessary here.
SeqCst is unnecessary here.
Relaxed is enough to make sure this `swap` results in `true` only once.
No need for SeqCst. Release+Acquire is the right memory ordering for a
mutex.
SeqCst is unnecessary. Release+Acquire is the right ordering for a
mutex.
Relaxed is enough to ensure fetch_add(1) returns each integer exactly
once.
SeqCst is unnecessary. Release+Acquire is the right ordering for a
mutex.
The SeqCst wasn't synchronizing with anything. Relaxed is enough.
@rustbot rustbot added O-unix Operating system: Unix-like O-wasm Target: WASM (WebAssembly), http://webassembly.org/ O-windows Operating system: Windows S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Mar 19, 2024
@m-ou-se m-ou-se added A-atomic Area: Atomics, barriers, and sync primitives C-cleanup Category: PRs that clean code up or issues documenting cleanup. and removed O-windows Operating system: Windows O-wasm Target: WASM (WebAssembly), http://webassembly.org/ O-unix Operating system: Unix-like labels Mar 19, 2024
@Amanieu
Copy link
Member

Amanieu commented Mar 19, 2024

Could we run the testsuite on miri just in case something was missed? It's easy to make mistakes with insufficient memory orderings.

Other than that, I strongly agree with the premise that SeqCst is an anti-pattern (except for fence(SeqCst) which has valid use cases).

@m-ou-se
Copy link
Member Author

m-ou-se commented Mar 19, 2024

@RalfJung how do we run Miri on the std test suite in this PR?

@RalfJung
Copy link
Member

RalfJung commented Mar 19, 2024

You can use rustup-toolchain-install-master to install the toolchain for the commit this PR is based on, adding all the components required for Miri. (Something like -c cargo -c rust-src -c miri should work.)

Then check out https://github.com/rust-lang/miri-test-libstd/, in there add an override for the above toolchain, and run something like

MIRI_LIB_SRC=<path to rustc checkout with this PR>/library ./run-test.sh alloc -- sync

The first argument is the crate to test. The rest (-- sync) is passed to cargo test (so if you have only a single filter the -- is not needed).

The std crate has some areas that don't work with Miri so a bunch of filtering will be needed. See here for the filtering we do in our daily runs.


It is advisable to see SeqCst as a warning sign. Seeing it in the wild often means that either something complicated is going on, or simply that the author did not take the time to analyze their memory ordering related assumptions, both of which are reasons for extra scrutiny.

I agree with that. However, I also would say the same about "Relaxed". These accesses are incredibly poorly behaved. (See llvm/llvm-project#64188 for a recent example of trouble they are causing for standard compiler optimizations.) IMO one should always use the release/acquire modes unless there is a strong reason to use anything else, and any deviation from this requires a justifying comment.

So I'm not happy to see a proliferation of "Relaxed" in this PR, in particular in docs.

Copy link
Member

@joboet joboet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, modulo the comment!

@m-ou-se
Copy link
Member Author

m-ou-se commented Mar 20, 2024

I also would say the same about "Relaxed". These accesses are incredibly poorly behaved. (See llvm/llvm-project#64188 for a recent example of trouble they are causing for standard compiler optimizations.) IMO one should always use the release/acquire modes unless there is a strong reason to use anything else, and any deviation from this requires a justifying comment.

That example you link is not just about a single atomic variable with relaxed operations, it's about an interaction of pointers+acquire-release+relaxed (and some variable whose address hasn't escaped/been exposed at the time of the release operation). Such a complex situation has very little to do with the simple cases we have in the standard library.

This PR is about very simple patterns, such as using fetch_add(1) for generating unique integers (ignoring overflow). Relaxed is perfectly fine for these.

SeqCst isn't necessary in any of these cases.
@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Mar 20, 2024
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request Mar 20, 2024
Relax SeqCst ordering in standard library.

Every single SeqCst in the standard library is unnecessary. In all cases, Relaxed or Release+Acquire was sufficient.

As I [wrote](https://marabos.nl/atomics/memory-ordering.html#common-misconceptions) in my book on atomics:

> [..] when reading code, SeqCst basically tells the reader: "this operation depends on the total order of every single SeqCst operation in the program," which is an incredibly far-reaching claim. The same code would likely be easier to review and verify if it used weaker memory ordering instead, if possible. For example, Release effectively tells the reader: "this relates to an acquire operation on the same variable," which involves far fewer considerations when forming an understanding of the code.
>
> It is advisable to see SeqCst as a warning sign. Seeing it in the wild often means that either something complicated is going on, or simply that the author did not take the time to analyze their memory ordering related assumptions, both of which are reasons for extra scrutiny.

r? `@Amanieu` `@joboet`
bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 20, 2024
…llaumeGomez

Rollup of 8 pull requests

Successful merges:

 - rust-lang#122494 (Simplify key-based thread locals)
 - rust-lang#122644 (pattern analysis: add a custom test harness)
 - rust-lang#122723 (Use same file permissions for ar_archive_writer as the LLVM archive writer)
 - rust-lang#122729 (Relax SeqCst ordering in standard library.)
 - rust-lang#122740 (use more accurate terminology)
 - rust-lang#122764 (coverage: Remove incorrect assertions from counter allocation)
 - rust-lang#122765 (Add `usize::MAX` arg tests for Vec)
 - rust-lang#122776 (Rename `hir::Let` into `hir::LetExpr`)

r? `@ghost`
`@rustbot` modify labels: rollup
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request Mar 20, 2024
Relax SeqCst ordering in standard library.

Every single SeqCst in the standard library is unnecessary. In all cases, Relaxed or Release+Acquire was sufficient.

As I [wrote](https://marabos.nl/atomics/memory-ordering.html#common-misconceptions) in my book on atomics:

> [..] when reading code, SeqCst basically tells the reader: "this operation depends on the total order of every single SeqCst operation in the program," which is an incredibly far-reaching claim. The same code would likely be easier to review and verify if it used weaker memory ordering instead, if possible. For example, Release effectively tells the reader: "this relates to an acquire operation on the same variable," which involves far fewer considerations when forming an understanding of the code.
>
> It is advisable to see SeqCst as a warning sign. Seeing it in the wild often means that either something complicated is going on, or simply that the author did not take the time to analyze their memory ordering related assumptions, both of which are reasons for extra scrutiny.

r? ``@Amanieu`` ``@joboet``
bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 20, 2024
…llaumeGomez

Rollup of 8 pull requests

Successful merges:

 - rust-lang#122644 (pattern analysis: add a custom test harness)
 - rust-lang#122696 (Add bare metal riscv32 target.)
 - rust-lang#122723 (Use same file permissions for ar_archive_writer as the LLVM archive writer)
 - rust-lang#122729 (Relax SeqCst ordering in standard library.)
 - rust-lang#122740 (use more accurate terminology)
 - rust-lang#122764 (coverage: Remove incorrect assertions from counter allocation)
 - rust-lang#122765 (Add `usize::MAX` arg tests for Vec)
 - rust-lang#122776 (Rename `hir::Let` into `hir::LetExpr`)

r? `@ghost`
`@rustbot` modify labels: rollup
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Mar 20, 2024
Relax SeqCst ordering in standard library.

Every single SeqCst in the standard library is unnecessary. In all cases, Relaxed or Release+Acquire was sufficient.

As I [wrote](https://marabos.nl/atomics/memory-ordering.html#common-misconceptions) in my book on atomics:

> [..] when reading code, SeqCst basically tells the reader: "this operation depends on the total order of every single SeqCst operation in the program," which is an incredibly far-reaching claim. The same code would likely be easier to review and verify if it used weaker memory ordering instead, if possible. For example, Release effectively tells the reader: "this relates to an acquire operation on the same variable," which involves far fewer considerations when forming an understanding of the code.
>
> It is advisable to see SeqCst as a warning sign. Seeing it in the wild often means that either something complicated is going on, or simply that the author did not take the time to analyze their memory ordering related assumptions, both of which are reasons for extra scrutiny.

r? ```@Amanieu``` ```@joboet```
bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 20, 2024
…iaskrgr

Rollup of 10 pull requests

Successful merges:

 - rust-lang#122545 (Ignore paths from expansion in `unused_qualifications`)
 - rust-lang#122644 (pattern analysis: add a custom test harness)
 - rust-lang#122696 (Add bare metal riscv32 target.)
 - rust-lang#122729 (Relax SeqCst ordering in standard library.)
 - rust-lang#122740 (use more accurate terminology)
 - rust-lang#122749 (make `type_flags(ReError) & HAS_ERROR`)
 - rust-lang#122764 (coverage: Remove incorrect assertions from counter allocation)
 - rust-lang#122765 (Add `usize::MAX` arg tests for Vec)
 - rust-lang#122776 (Rename `hir::Let` into `hir::LetExpr`)
 - rust-lang#122786 (compiletest: Introduce `remove_and_create_dir_all()` helper)

r? `@ghost`
`@rustbot` modify labels: rollup
bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 21, 2024
Rollup of 8 pull requests

Successful merges:

 - rust-lang#122545 (Ignore paths from expansion in `unused_qualifications`)
 - rust-lang#122729 (Relax SeqCst ordering in standard library.)
 - rust-lang#122740 (use more accurate terminology)
 - rust-lang#122749 (make `type_flags(ReError) & HAS_ERROR`)
 - rust-lang#122764 (coverage: Remove incorrect assertions from counter allocation)
 - rust-lang#122765 (Add `usize::MAX` arg tests for Vec)
 - rust-lang#122776 (Rename `hir::Let` into `hir::LetExpr`)
 - rust-lang#122786 (compiletest: Introduce `remove_and_create_dir_all()` helper)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 43ad753 into rust-lang:master Mar 21, 2024
11 checks passed
@rustbot rustbot added this to the 1.79.0 milestone Mar 21, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Mar 21, 2024
Rollup merge of rust-lang#122729 - m-ou-se:relax, r=Amanieu

Relax SeqCst ordering in standard library.

Every single SeqCst in the standard library is unnecessary. In all cases, Relaxed or Release+Acquire was sufficient.

As I [wrote](https://marabos.nl/atomics/memory-ordering.html#common-misconceptions) in my book on atomics:

> [..] when reading code, SeqCst basically tells the reader: "this operation depends on the total order of every single SeqCst operation in the program," which is an incredibly far-reaching claim. The same code would likely be easier to review and verify if it used weaker memory ordering instead, if possible. For example, Release effectively tells the reader: "this relates to an acquire operation on the same variable," which involves far fewer considerations when forming an understanding of the code.
>
> It is advisable to see SeqCst as a warning sign. Seeing it in the wild often means that either something complicated is going on, or simply that the author did not take the time to analyze their memory ordering related assumptions, both of which are reasons for extra scrutiny.

r? ````@Amanieu```` ````@joboet````
@RalfJung
Copy link
Member

RalfJung commented Mar 21, 2024

@m-ou-se btw, given your stance on SeqCst (which I agree with), I wonder if you'd be open to reviving rust-lang/rfcs#2503 in one shape or another? IOW, providing some way to say "give me release and/or acquire, whatever fits for this operation", without the risk of accidentally using Acquire/Release for the success case of an RMW operation (which subtly introduces relaxed semantics)?

@m-ou-se m-ou-se deleted the relax branch March 21, 2024 06:50
@m-ou-se
Copy link
Member Author

m-ou-se commented Mar 21, 2024

@RalfJung I've seen a lot of cases where people want (or think they want ^^') an "acquire store" or "release load" (which don't exist). With something like Ordering::AutoAcqOrRel, it becomes a lot less clear which operations are acquire vs which ones are release. So I don't think it's a good idea to hide that difference.

@RalfJung
Copy link
Member

Hm, yes that is fair. I think I am mostly worried about accidentally using Acquire/Release as the success ordering of an RMW, which introduces Relaxed semantics without Relaxed appearing in the code.

@m-ou-se
Copy link
Member Author

m-ou-se commented Mar 21, 2024

I imagine we could have fetch_add(1, Ordering::AcquireRelaxed), with a lint to discourage Ordering::Acquire.. But AcquireRelaxed sounds almost like AcqRel. 😬 Anyway, let's move this discussion to Zulip or something. ^^'

Edit: Zulip: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Explicit.20Relaxed.20ordering.20in.20RMW.20operations/near/428083271

Thomasdezeeuw added a commit to Thomasdezeeuw/heph that referenced this pull request Apr 14, 2024
This replaces all SeqCst atomic operations with Release, Acquire or
AcqRel.

Also see rust-lang/rust#122729.
Thomasdezeeuw added a commit to Thomasdezeeuw/heph that referenced this pull request Apr 14, 2024
This replaces all SeqCst atomic operations with Release, Acquire or
AcqRel.

Also see rust-lang/rust#122729.
Thomasdezeeuw added a commit to Thomasdezeeuw/heph that referenced this pull request Apr 14, 2024
This replaces all SeqCst atomic operations with Release, Acquire or
AcqRel.

Also see rust-lang/rust#122729.
Thomasdezeeuw added a commit to Thomasdezeeuw/heph that referenced this pull request Apr 14, 2024
This replaces all SeqCst atomic operations with Release, Acquire or
AcqRel.

Also see rust-lang/rust#122729.
Thomasdezeeuw added a commit to Thomasdezeeuw/heph that referenced this pull request Apr 14, 2024
This replaces all SeqCst atomic operations with Release, Acquire or
AcqRel.

Also see rust-lang/rust#122729.
Thomasdezeeuw added a commit to Thomasdezeeuw/heph that referenced this pull request Apr 14, 2024
This replaces all SeqCst atomic operations with Release, Acquire or
AcqRel.

Also see rust-lang/rust#122729.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-atomic Area: Atomics, barriers, and sync primitives C-cleanup Category: PRs that clean code up or issues documenting cleanup. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants