-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Add fast WaitOnAddress-based thread parker for Windows. #77618
Add fast WaitOnAddress-based thread parker for Windows. #77618
Conversation
- Clarify memory ordering and spurious wakeups.
r? @kennytm (rust_highfive has picked a reviewer for you, use r? to override) |
@rustbot modify labels: +T-libs +O-windows +A-concurrency |
@retep998 If I understood correctly, you're the right person to ask about Windows APIs in Rust? |
AFAICT this looks correct. Note that an acquire operation only synchronizes with the most recent release operation on a particular atomic variable (see LLVM Lang spec), so two |
@rustbot ping windows |
Hey Windows Group! This bug has been identified as a good "Windows candidate". cc @arlosi @danielframpton @gdr-at-ms @kennykerr @luqmana @lzybkr @retep998 @rylev @sivadeilra |
I really cannot condone the use of undocumented APIs. I would highly recommend dropping support for Windows XP. There are so many great concurrency APIs that are available starting with Windows 7. This would dramatically simplify the code and avoid the need to dynamically load anything. I did however want to comment on the |
Windows 7 does not have anything futex-like (like WaitOnAddress for Windows 8+) though. Do you think there's a significant difference between fully dropping support for XP (and Vista, etc.) versus a best-effort implementation using this undocumented API? Lots of Rust software already uses this API, including Rustc itself, through
Thanks! Opened #78444 for that. |
I think I haven't looked too closely at the Rust thread parker requirements, but why not you just use an event e.g. @oldnewthing may also have some suggestions for |
For Vista+ there's SlimRW/ConditionVariable? I think they're basically wrappers around keyed events anyway, no? |
That's correct. I wrote about that here. |
Yeah, that's what Rust's
The |
Replacing keyed events with the classic Edit: Updated for per-parker event handles |
Uh, that uses a single event object for all thread parkers, without using anything as key. How would that work? How would unpark() cause the right park()'ed thread to wake up? |
Ah, true. Hmm. What's the cost of an extra Handle in Parker? Not quite as slim as the futex impls but still better than the generic one. |
To summarise some assorted thoughts based on discussions here and elsewhere: One of the issues with the current compatibility shim is that it hard codes legacy dll names. Loading of the new sync APIs doesn't have this issue as it uses the API set ("api-ms-win-core-synch-l1-2-0"). Another issue is that loading a module restricts where Rust code can be used. However, if this dynamic loading becomes limited to park/unpark then it at least reduces the chances of this being an issue in practice (though park is used by Using undocumented ntdll functions is more controversial. Perhaps it would help if this was gated by an explicit runtime check of the OS version? I mean, people still aren't going to be comfortable with this but at least it would be explicitly limited. These issues may be solved more satisfactorily in the future by either a |
They're not really legacy DLL names - they're the actual DLL names. The API sets are just forwarders but the OS loader can figure it out either way. The actual DLL names are preferred IMO because they work down-level before API sets were introduced. The official onecore and onecoreuap libs we use internally and provide through the SDK redirect to the actual DLL names where possible. Dynamic loading is a normal part of Windows programming. Version checks on the other hand are extremely bad for reliability and compat. I'd sooner recommend the undocumented APIs than have you start checking version numbers. 😉 |
Ah, elsewhere it was mentioned that hard coding dll names like "kernel32" made things more difficult in the lab test when the OS being used doesn't have kernel32. |
r? @dtolnay |
@dtolnay any updates on reviewing this? |
I am not confident enough about the tradeoffs here — it would be good to get some additional sets of critical eyes on this implementation from the libs team. @rust-lang/libs |
Team member @dtolnay has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
I like the fact that this matches Wine and ReactOS. If we're doing what they're doing, then I think we're in good company. However, I am a bit skeptical of adding support for Windows XP where it seems like none existed before? I don't understand everything involved here, but it sounds like the XP support is incidental? That is, even if we didn't endeavor to add XP support, we would still get it by virtue of supporting Windows 7. Am I understanding that correctly? |
Yes. |
I don't think an FCP is needed here since this doesn't change any user-visible APIs. I reviewed both code paths and they look correct to me, so I'm just going to merge this as it is. @bors r+ |
📌 Commit 03fb61c has been approved by |
☀️ Test successful - checks-actions |
fn keyed_event_handle() -> c::HANDLE { | ||
const INVALID: usize = !0; | ||
static HANDLE: AtomicUsize = AtomicUsize::new(INVALID); | ||
match HANDLE.load(Relaxed) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May need use atomic init_once to guard this, stop calling NtCreateKeyedEvent multiple times
This adds a fast futex-based thread parker for Windows. It either uses WaitOnAddress+WakeByAddressSingle or NT Keyed Events (NtWaitForKeyedEvent+NtReleaseKeyedEvent), depending on which is available. Together, this makes this thread parker work for Windows XP and up. Before this change, park()/unpark() did not work on Windows XP: it needs condition variables, which only exist since Windows Vista.
Unfortunately, NT Keyed Events are an undocumented Windows API. However:
[1]: http://www.locklessinc.com/articles/keyed_events/
[2]: Amanieu/parking_lot@43abbc964e
[3]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c
[4]: Windows Internals, Part 1, ISBN 9780735671300
The choice of fallback API is inspired by parking_lot(_core), but the implementation of this thread parker is different. While parking_lot has no use for a fast path (park() directly returning if unpark() was already called), this implementation has a fast path that returns without even checking which waiting/waking API to use, as the same atomic variable with compatible states is used in all cases.