-
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
std: refactor pthread
-based synchronization
#128184
Conversation
r? @ChrisDenton rustbot has assigned @ChrisDenton. Use |
This comment has been minimized.
This comment has been minimized.
☔ The latest upstream changes (presumably #125443) made this pull request unmergeable. Please resolve the merge conflicts. |
88c22d5
to
194cd19
Compare
This looks good to me on the surface but I don't feel I can confidently r+ atm as I don't currently have the time to go over it properly. So I'll reroll. r? libs |
This comment has been minimized.
This comment has been minimized.
d9e06c4
to
1cf7f38
Compare
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.
So, it seems like this is going to be a big diff either way. But is there any chance you can do the "coalesce in a single spot" and "change up various internals" in separate PRs? Or at least commits?
In particular, if possible, I'd like for the LazyBox -> OnceBox to be separately bisectable, even if it means I review somewhat gnarlier code.
☔ The latest upstream changes (presumably #130724) made this pull request unmergeable. Please resolve the merge conflicts. |
This PR replaces the `LazyBox` wrapper used to allocate the pthread primitives with `OnceBox`, which has a more familiar API mirroring that of `OnceLock`. This cleans up the code in preparation for larger changes like rust-lang#128184 (from which this PR was split) and allows some neat optimizations, like avoid an acquire-load of the allocation pointer in `Mutex::unlock`, where the initialization of the allocation must have already been observed. Additionally, I've gotten rid of the TEEOS `Condvar` code, it's just a duplicate of the pthread one anyway and I didn't want to repeat myself.
This PR replaces the `LazyBox` wrapper used to allocate the pthread primitives with `OnceBox`, which has a more familiar API mirroring that of `OnceLock`. This cleans up the code in preparation for larger changes like rust-lang#128184 (from which this PR was split) and allows some neat optimizations, like avoid an acquire-load of the allocation pointer in `Mutex::unlock`, where the initialization of the allocation must have already been observed. Additionally, I've gotten rid of the TEEOS `Condvar` code, it's just a duplicate of the pthread one anyway and I didn't want to repeat myself.
This PR replaces the `LazyBox` wrapper used to allocate the pthread primitives with `OnceBox`, which has a more familiar API mirroring that of `OnceLock`. This cleans up the code in preparation for larger changes like rust-lang#128184 (from which this PR was split) and allows some neat optimizations, like avoid an acquire-load of the allocation pointer in `Mutex::unlock`, where the initialization of the allocation must have already been observed. Additionally, I've gotten rid of the TEEOS `Condvar` code, it's just a duplicate of the pthread one anyway and I didn't want to repeat myself.
std: replace `LazyBox` with `OnceBox` This PR replaces the `LazyBox` wrapper used to allocate the pthread primitives with `OnceBox`, which has a more familiar API mirroring that of `OnceLock`. This cleans up the code in preparation for larger changes like rust-lang#128184 (from which this PR was split) and allows some neat optimizations, like avoid an acquire-load of the allocation pointer in `Mutex::unlock`, where the initialization of the allocation must have already been observed. Additionally, I've gotten rid of the TEEOS `Condvar` code, it's just a duplicate of the pthread one anyway and I didn't want to repeat myself.
Rollup merge of rust-lang#131094 - joboet:lazy_once_box, r=ibraheemdev std: replace `LazyBox` with `OnceBox` This PR replaces the `LazyBox` wrapper used to allocate the pthread primitives with `OnceBox`, which has a more familiar API mirroring that of `OnceLock`. This cleans up the code in preparation for larger changes like rust-lang#128184 (from which this PR was split) and allows some neat optimizations, like avoid an acquire-load of the allocation pointer in `Mutex::unlock`, where the initialization of the allocation must have already been observed. Additionally, I've gotten rid of the TEEOS `Condvar` code, it's just a duplicate of the pthread one anyway and I didn't want to repeat myself.
The non-trivial code for `pthread_condvar` is duplicated across the thread parking and the `Mutex`/`Condvar` implementations. This PR moves that code into `sys::pal`, which now exposes a non-movable wrapper type for `pthread_mutex_t` and `pthread_condvar_t`.
1cf7f38
to
528b37a
Compare
@bors try |
std: refactor `pthread`-based synchronization The non-trivial code for `pthread_condvar` is duplicated across the thread parking and the `Mutex`/`Condvar` implementations. This PR moves that code into `sys::pal`, which now exposes an `unsafe` wrapper type for `pthread_mutex_t` and `pthread_condvar_t`. try-job: dist-various-1 try-job: dist-various-2 try-job: test-various try-job: armhf-linux try-job: aarch64-gnu try-job: x86_64-fuchsia try-job: dist-i686-linux try-job: aarch64-apple try-job: dist-android try-job: dist-x86_64-freebsd
This comment has been minimized.
This comment has been minimized.
@bors try |
std: refactor `pthread`-based synchronization The non-trivial code for `pthread_condvar` is duplicated across the thread parking and the `Mutex`/`Condvar` implementations. This PR moves that code into `sys::pal`, which now exposes an `unsafe` wrapper type for `pthread_mutex_t` and `pthread_condvar_t`. try-job: dist-various-1 try-job: dist-various-2 try-job: test-various try-job: armhf-gnu try-job: aarch64-gnu try-job: x86_64-fuchsia try-job: dist-i686-linux try-job: aarch64-apple try-job: dist-android try-job: dist-x86_64-freebsd
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.
Sorry about taking so long, it was kinda hard to follow all the moves.
We need to get more pedantic.
self.cvar.get_or_init(|| { | ||
let mut cvar = Box::pin(pal::Condvar::new()); | ||
// SAFETY: we only call `init` once, namely here. | ||
unsafe { cvar.as_mut().init() }; | ||
cvar | ||
}) |
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.
So, as I understand it, the closure can potentially get called twice, as the sync can race. That means that init
has to be safe to call twice, even if it means that it can be "safe, but only if you immediately destroy it if it's a copy". That means that this // SAFETY
comment is somewhat off-base.
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.
The invariant is that init
may only be called once per object; that isn't violated here.
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
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.
I pried it apart.
I don't like "per object" as a constraint, because we have objects that manage objects, and we may create two objects to put inside one object, and then we have to get rid of one object, but we keep the other object, because we put it inside of the object...
... ... ...you get the picture.
What matters is that the pointee only sees one init.
☀️ Try build successful - checks-actions |
Passed on these jobs:
|
To put it another way:
Thinking again, my understanding is that what happens here, for both types, can be, hypothetically:
As I understand it, this is sound, but we have objectively run the initialization function twice, and thus the safety comment is not really the clearest. We can't even say "once per condvar" or "once per mutex", because of how we have three different things we are calling a "mutex" or "condvar", so it's ambiguous, and "the thing we can only initialize once" is each place in memory. The job of this implementation is only to present the appearance that we've only allocated and initialized one This is all fine. I just would like this to be clear from the comments, because we are three layers deep in terms of signifier vs. signified, we have indirections on top of indirections. The safety comments should be written more from the "insider perspective", since they're, well, inside the implementation. Footnotes
|
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.
A possible diff that removes the ambiguity.
/// Must only be called once. | ||
pub unsafe fn init(self: Pin<&mut Self>) { |
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.
/// Must only be called once. | |
pub unsafe fn init(self: Pin<&mut Self>) { | |
/// This value must only be initialized once. | |
pub unsafe fn init(self: Pin<&mut Self>) { |
/// # Safety | ||
/// May only be called once. |
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.
/// # Safety | |
/// May only be called once. | |
/// # Safety | |
/// This value must only be initialized once. |
const CLOCK: libc::clockid_t = libc::CLOCK_MONOTONIC; | ||
|
||
/// # Safety | ||
/// May only be called once. |
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 only be called once. | |
/// This value must only be initialized once. |
fn get(&self) -> Pin<&pal::Condvar> { | ||
self.cvar.get_or_init(|| { | ||
let mut cvar = Box::pin(pal::Condvar::new()); | ||
// SAFETY: we only call `init` once, namely here. |
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.
// SAFETY: we only call `init` once, namely here. | |
// SAFETY: pointee is initialized exactly once, by this closure |
// This is sound however, as it cannot have been locked. | ||
self.pal.get_or_init(|| { | ||
let mut pal = Box::pin(pal::Mutex::new()); | ||
// SAFETY: we only call `init` once, namely here. |
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.
// SAFETY: we only call `init` once, namely here. | |
// SAFETY: pointee is initialized exactly once, by this closure |
It's less about places in memory (you could safely destroy a |
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.
thank you.
@bors r+ |
…iaskrgr Rollup of 8 pull requests Successful merges: - rust-lang#128184 (std: refactor `pthread`-based synchronization) - rust-lang#132047 (Robustify and genericize return-type-notation resolution in `resolve_bound_vars`) - rust-lang#133515 (fix: hurd build, stat64.st_fsid was renamed to st_dev) - rust-lang#133602 (fix: fix codeblocks in `PathBuf` example) - rust-lang#133622 (update link to "C++ Exceptions under the hood" blog) - rust-lang#133660 (Do not create trait object type if missing associated types) - rust-lang#133686 (Add diagnostic item for `std::ops::ControlFlow`) - rust-lang#133689 (Fixed typos by changing `happend` to `happened`) r? `@ghost` `@rustbot` modify labels: rollup
Rollup merge of rust-lang#128184 - joboet:refactor_pthread_sync, r=workingjubilee std: refactor `pthread`-based synchronization The non-trivial code for `pthread_condvar` is duplicated across the thread parking and the `Mutex`/`Condvar` implementations. This PR moves that code into `sys::pal`, which now exposes an `unsafe` wrapper type for `pthread_mutex_t` and `pthread_condvar_t`.
The non-trivial code for
pthread_condvar
is duplicated across the thread parking and theMutex
/Condvar
implementations. This PR moves that code intosys::pal
, which now exposes anunsafe
wrapper type forpthread_mutex_t
andpthread_condvar_t
.