Add more clarification to spin_loop docs - #159679
Conversation
|
r? @aapoalas rustbot has assigned @aapoalas. Use Why was this reviewer chosen?The reviewer was selected based on:
|
| /// [hardware threads]: https://en.wikipedia.org/wiki/Simultaneous_multithreading | ||
| /// | ||
| /// Because the operating system is not notified at all, this hint should be used only | ||
| /// when the expected wait time is short, i.e. a few instructions. Additionally, since |
There was a problem hiding this comment.
I think talking about "expected wait time" here is a red herring.
- If you're waiting on another thread, then there is generally no way to get a useful bound on how long it might take until that other thread releases the lock because that's up to the OS scheduler. Especially if you're spinning and thus potentially preventing the other thread from being scheduled. Critical section length in instructions or clock cycles has nothing to do with it.
- If you're doing a non-blocking CAS or LL/SC loop (like
Atomic*::updateis intended for), then the duration of the operation that the loop tried to does matter, but only because longer update sequences marginally increase the likelihood and cost of contention. In any case, such loops shouldn't usespin_loopin the first place (as the rest of this paragraph explains).
There was a problem hiding this comment.
Conversely: some mutex implementations (including std's futex-based one) happily do the optimistic N-iterations-of spinning even though they have no idea how long the critical sections protected by any particular mutex are. That's fine because it's a still a win when it works out, not too costly compared to the syscall when it doesn't work out, and the fallback to the syscall is still needed in any case.
There was a problem hiding this comment.
I see the "only when expected wait time is short" point remained; I still think it's a red herring that distracts from the other (correct) points being made. In addition, many people have already internalized the folklore that "spin locks are great for short critical sections" and I'm worried about implicitly reinforcing it by mentioning something that sounds related.
|
Reminder, once the PR becomes ready for a review, use |
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
So, finally getting back to this after sitting on it for a while. I agree with everything you said, but since documentation for atomics is often very technical and poorly explained, I wanted to try and come up with something a bit more digestible without compromising accuracy. Hopefully the new version covers the feedback you gave a bit better. |
|
@rustbot review |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Thanks, I like this direction and agree it helps make it more digestible. There's still two points (one new, one old) where I think this is inaccurate and could encourage a wrong mental model.
@rustbot author
| /// Be careful when using this hint, since improper usage can lead to [priority inversion] | ||
| /// and halt progress entirely. For example, take the below implementation of [`update`]: |
There was a problem hiding this comment.
I can see the argument for trying to tie this point to"improper usage of spin_loop" as that's the method being documented here. But I think it's a bit misleading to attribute the problem to using spin_loop rather than to busy-waiting at all, especially when immediately followed by an example that illustrates a different reason to not use spin_loop. It would be unfortunate if someone read this and concluded that they should remove spin_loop from their spin-lock-esque loop.
An easy fix would be to move the mention of priority inversion to the paragraph below that talks about use in mutexes. But if you're up for it, I could see splitting this up into two separate examples:
- Show a very simple busy-looping spin lock that uses
spin_loop(just the inner loop trying to acquire the lock, doesn't need to be runnable), and mention the problems with that1. This would go together with the discussion of how proper lock implementations usespin_loopand why they still yield to the OS after some attempts. - Explain that this is only for busy-wait loops that wait on another thread, using the
Atomic::updateexample and existing discussion.
Footnotes
-
Priority inversion for one, but if you're up for it, it would also be great to mention the terrible performance even for same-priority threads if the thread that has acquired the lock is not currently running. ↩
| /// and halt progress entirely. For example, take the below implementation of [`update`]: | ||
| /// | ||
| /// [priority inversion]: https://en.wikipedia.org/wiki/Priority_inversion | ||
| /// [`update`]: crate::sync::atomic::Atomic::update |
There was a problem hiding this comment.
nit: Atomic and the generic methods on it are still unstable, so linking there is a bit weird for users of stable. Maybe just link AtomicU32::update specifically?
There was a problem hiding this comment.
Yeah, was thinking about that and not sure. I can go with the u32 version
| /// [hardware threads]: https://en.wikipedia.org/wiki/Simultaneous_multithreading | ||
| /// | ||
| /// Because the operating system is not notified at all, this hint should be used only | ||
| /// when the expected wait time is short, i.e. a few instructions. Additionally, since |
There was a problem hiding this comment.
I see the "only when expected wait time is short" point remained; I still think it's a red herring that distracts from the other (correct) points being made. In addition, many people have already internalized the folklore that "spin locks are great for short critical sections" and I'm worried about implicitly reinforcing it by mentioning something that sounds related.
Follow-up to #159121.
Attempting to update the
spin_loopdocs to make it a little more clear what the use case for this hint is. Also uses the term "hardware thread" since "hyper-thread" is an Intel-specific term.