Uh oh!
There was an error while loading. Please reload this page.
Add fast WaitOnAddress-based thread parker for Windows. - #77618
Conversation
- Clarify memory ordering and spurious wakeups.
rust-highfive
commented
Oct 6, 2020
r? @kennytm (rust_highfive has picked a reviewer for you, use r? to override) |
m-ou-se
commented
Oct 6, 2020
@rustbot modify labels: +T-libs +O-windows +A-concurrency |
m-ou-se
commented
Oct 8, 2020
@retep998 If I understood correctly, you're the right person to ask about Windows APIs in Rust? |
programmerjake
commented
Oct 19, 2020
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 |
m-ou-se
commented
Oct 27, 2020
@rustbot ping windows |
rustbot
commented
Oct 27, 2020
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 |
kennykerr
commented
Oct 27, 2020
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 |
m-ou-se
commented
Oct 27, 2020
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. |
kennykerr
commented
Oct 27, 2020
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 |
ChrisDenton
commented
Oct 27, 2020
For Vista+ there's SlimRW/ConditionVariable? I think they're basically wrappers around keyed events anyway, no? |
kennykerr
commented
Oct 27, 2020
That's correct. I wrote about that here. |
m-ou-se
commented
Oct 27, 2020
Yeah, that's what Rust's
The |
Replacing keyed events with the classic Edit: Updated for per-parker event handles |
m-ou-se
commented
Oct 27, 2020
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? |
luqmana
commented
Oct 27, 2020
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. |
ChrisDenton
commented
Oct 29, 2020
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 |
kennykerr
commented
Oct 29, 2020
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. 😉 |
ChrisDenton
commented
Oct 29, 2020
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. |
Dylan-DPC-zz
commented
Nov 20, 2020
r? @dtolnay |
Dylan-DPC-zz
commented
Dec 8, 2020
@dtolnay any updates on reviewing this? |
dtolnay
commented
Dec 14, 2020
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. |
BurntSushi
commented
Dec 14, 2020
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? |
m-ou-se
commented
Dec 14, 2020
Yes. |
Amanieu
commented
Dec 14, 2020
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+ |
bors
commented
Dec 14, 2020
📌 Commit 03fb61c has been approved by |
bors
commented
Dec 14, 2020
bors
commented
Dec 14, 2020
☀️ 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.
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.