-
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
[core] add Exclusive
to sync
#97629
[core] add Exclusive
to sync
#97629
Conversation
Hey! It looks like you've submitted a new PR for the library teams! If this PR contains changes to any Examples of
|
r? @m-ou-se (rust-highfive has picked a reviewer for you, use r? to override) |
r? rust-lang/libs-api @rustbot label +T-libs-api -T-libs |
library/core/src/sync/exclusive.rs
Outdated
impl<T> Exclusive<T> { | ||
/// Wrap a value in an `Exclusive` | ||
#[unstable(feature = "exclusive_struct", issue = "none")] | ||
pub fn new(t: T) -> 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.
pub fn new(t: T) -> Self { | |
pub const fn new(t: T) -> 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.
good point! actually on unstable pretty much everything can be made const
! I am not sure if I need to add attributes to make stabilization easier though...
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 like this proposal very much 🙂
@guswynn Can you update the top comment to include a complete API overview? So just the types, methods, and trait impl blocks, without implementation or doc comments. (Just like we try to do on tracking issues.) That makes it a bit easier for reviewers to quickly get an idea of what API this PR is adding. |
@m-ou-se let me know if thats the format you wanted! |
@danielhenrymantilla the future impl doesnt work with
which is unclear to me, should I add a |
Yes, anything that operates through indirection can have a |
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
self.get_pin_mut().poll(cx) | ||
} | ||
} |
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.
Could we have impls of FnMut
and FnOnce
as well?
Ditto for Read
, Write
, and basically any stdlib trait with &mut self
methods exclusively
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.
Note that Read::is_read_vectored
and Write::is_write_vectored
take &self
, so those would both have to remain their default false
rather than forwarding inward.
We could generalize that principle to implement traits where all required methods are owned or mutable, leaving any &self
methods to their trait default. That could include Iterator
, for instance, implementing next
but not size_hint
. I'm not sure how far we should go with that, given that you can use get_mut
or into_inner
for direct access anyway.
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.
True, we could for starters limit ourselves to Fn{Mut,Once}
, then, before considering the others which can always have a manual impl through a wrapper struct: not only are these the only traits stable users can't implement themselves, it's also sufficiently simple traits for them being implemented to be non-controversial.
- A follow-up PR for the remaining traits would be easy to make 🙂
And even beyond manual impls on wrapper structs, we can also use ad-hoc closures and from_fn
-like constructors (currently only for Iterator
):
let mut sync_fn = move || exclusive_fn(); // wouldn't be needed with the fn impls
let mut sync_fut = async move { exclusive_fut.await }; // isn't needed thanks to the future impl
let mut sync_iterator = iter::from_fn(move || exclusive_iterator.next());
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.
hmm, its a bit weird to me to only be able to forward some methods. The guidance we decide on would be: "if all the required methods only require &mut
, then its okay to implement", as @cuviper mentioned, but i am tempted to only implement the ones that offer full functionality (Future
, FnOnce
, FnMut
), or none of them
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 agree that FnOnce
and FnMut
are compelling for their simplicity and that they are unstable for users. It also relates to this forum thread, although I think they probably don't need the direct trait, per se.
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.
Aren't impl's like these easy to add instantly-stable in the future?
Last week in the libs team meeting, we discussed a new experimental process for adding unstable features. Unlike an RFC or stabilization, we don't need a sign-off of the full team for adding an unstable API. But unlike a regular PR, we do want some time to allow for people to signal any concerns with a new API before merging it. I believe we haven't yet settled on a name for this 10 day not-yet-final comment period, so for now I'll call it the Unnamed Comment Period (UCP). I'd like to try it out on this PR. I think this API is worth trying out as an unstable feature, and the api overview looks good to me. So, since we don't have a bot for this yet... @m-ou-se ucp merge |
@m-ou-se its been 10 days, is this merge-able? |
Yes! Only five days late. We should probably replace me doing this manually by a bot that doesn't have adhd. :D |
🤖 📣 The <unnamed> comment period, with a disposition to merge, as per the review above, is now complete. As the This will be merged soon. |
library/core/src/sync/exclusive.rs
Outdated
/// | ||
/// | ||
/// [`Sync`]: core::marker::Sync | ||
#[unstable(feature = "exclusive_wrapper", issue = "none")] |
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.
Can you open a tracking issue? Thanks!
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.
yes!
@bors r+ |
📌 Commit 029f9aa has been approved by |
[core] add `Exclusive` to sync (discussed here: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Adding.20.60SyncWrapper.60.20to.20std) `Exclusive` is a wrapper that exclusively allows mutable access to the inner value if you have exclusive access to the wrapper. It acts like a compile time mutex, and hold an unconditional `Sync` implementation. ## Justification for inclusion into std - This wrapper unblocks actual problems: - The example that I hit was a vector of `futures::future::BoxFuture`'s causing a central struct in a script to be non-`Sync`. To work around it, you either write really difficult code, or wrap the futures in a needless mutex. - Easy to maintain: this struct is as simple as a wrapper can get, and its `Sync` implementation has very clear reasoning - Fills a gap: `&/&mut` are to `RwLock` as `Exclusive` is to `Mutex` ## Public Api ```rust // core::sync #[derive(Default)] struct Exclusive<T: ?Sized> { ... } impl<T: ?Sized> Sync for Exclusive {} impl<T> Exclusive<T> { pub const fn new(t: T) -> Self; pub const fn into_inner(self) -> T; } impl<T: ?Sized> Exclusive<T> { pub const fn get_mut(&mut self) -> &mut T; pub const fn get_pin_mut(Pin<&mut self>) -> Pin<&mut T>; pub const fn from_mut(&mut T) -> &mut Exclusive<T>; pub const fn from_pin_mut(Pin<&mut T>) -> Pin<&mut Exclusive<T>>; } impl<T: Future> Future for Exclusive { ... } impl<T> From<T> for Exclusive<T> { ... } impl<T: ?Sized> Debug for Exclusive { ... } ``` ## Naming This is a big bikeshed, but I felt that `Exclusive` captured its general purpose quite well. ## Stability and location As this is so simple, it can be in `core`. I feel that it can be stabilized quite soon after it is merged, if the libs teams feels its reasonable to add. Also, I don't really know how unstable feature work in std/core's codebases, so I might need help fixing them ## Tips for review The docs probably are the thing that needs to be reviewed! I tried my best, but I'm sure people have more experience than me writing docs for `Core` ### Implementation: The API is mostly pulled from https://docs.rs/sync_wrapper/latest/sync_wrapper/struct.SyncWrapper.html (which is apache 2.0 licenesed), and the implementation is trivial: - its an unsafe justification for pinning - its an unsafe justification for the `Sync` impl (mostly reasoned about by `@danielhenrymantilla` here: Actyx/sync_wrapper#2) - and forwarding impls, starting with derivable ones and `Future`
[core] add `Exclusive` to sync (discussed here: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Adding.20.60SyncWrapper.60.20to.20std) `Exclusive` is a wrapper that exclusively allows mutable access to the inner value if you have exclusive access to the wrapper. It acts like a compile time mutex, and hold an unconditional `Sync` implementation. ## Justification for inclusion into std - This wrapper unblocks actual problems: - The example that I hit was a vector of `futures::future::BoxFuture`'s causing a central struct in a script to be non-`Sync`. To work around it, you either write really difficult code, or wrap the futures in a needless mutex. - Easy to maintain: this struct is as simple as a wrapper can get, and its `Sync` implementation has very clear reasoning - Fills a gap: `&/&mut` are to `RwLock` as `Exclusive` is to `Mutex` ## Public Api ```rust // core::sync #[derive(Default)] struct Exclusive<T: ?Sized> { ... } impl<T: ?Sized> Sync for Exclusive {} impl<T> Exclusive<T> { pub const fn new(t: T) -> Self; pub const fn into_inner(self) -> T; } impl<T: ?Sized> Exclusive<T> { pub const fn get_mut(&mut self) -> &mut T; pub const fn get_pin_mut(Pin<&mut self>) -> Pin<&mut T>; pub const fn from_mut(&mut T) -> &mut Exclusive<T>; pub const fn from_pin_mut(Pin<&mut T>) -> Pin<&mut Exclusive<T>>; } impl<T: Future> Future for Exclusive { ... } impl<T> From<T> for Exclusive<T> { ... } impl<T: ?Sized> Debug for Exclusive { ... } ``` ## Naming This is a big bikeshed, but I felt that `Exclusive` captured its general purpose quite well. ## Stability and location As this is so simple, it can be in `core`. I feel that it can be stabilized quite soon after it is merged, if the libs teams feels its reasonable to add. Also, I don't really know how unstable feature work in std/core's codebases, so I might need help fixing them ## Tips for review The docs probably are the thing that needs to be reviewed! I tried my best, but I'm sure people have more experience than me writing docs for `Core` ### Implementation: The API is mostly pulled from https://docs.rs/sync_wrapper/latest/sync_wrapper/struct.SyncWrapper.html (which is apache 2.0 licenesed), and the implementation is trivial: - its an unsafe justification for pinning - its an unsafe justification for the `Sync` impl (mostly reasoned about by ``@danielhenrymantilla`` here: Actyx/sync_wrapper#2) - and forwarding impls, starting with derivable ones and `Future`
[core] add `Exclusive` to sync (discussed here: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Adding.20.60SyncWrapper.60.20to.20std) `Exclusive` is a wrapper that exclusively allows mutable access to the inner value if you have exclusive access to the wrapper. It acts like a compile time mutex, and hold an unconditional `Sync` implementation. ## Justification for inclusion into std - This wrapper unblocks actual problems: - The example that I hit was a vector of `futures::future::BoxFuture`'s causing a central struct in a script to be non-`Sync`. To work around it, you either write really difficult code, or wrap the futures in a needless mutex. - Easy to maintain: this struct is as simple as a wrapper can get, and its `Sync` implementation has very clear reasoning - Fills a gap: `&/&mut` are to `RwLock` as `Exclusive` is to `Mutex` ## Public Api ```rust // core::sync #[derive(Default)] struct Exclusive<T: ?Sized> { ... } impl<T: ?Sized> Sync for Exclusive {} impl<T> Exclusive<T> { pub const fn new(t: T) -> Self; pub const fn into_inner(self) -> T; } impl<T: ?Sized> Exclusive<T> { pub const fn get_mut(&mut self) -> &mut T; pub const fn get_pin_mut(Pin<&mut self>) -> Pin<&mut T>; pub const fn from_mut(&mut T) -> &mut Exclusive<T>; pub const fn from_pin_mut(Pin<&mut T>) -> Pin<&mut Exclusive<T>>; } impl<T: Future> Future for Exclusive { ... } impl<T> From<T> for Exclusive<T> { ... } impl<T: ?Sized> Debug for Exclusive { ... } ``` ## Naming This is a big bikeshed, but I felt that `Exclusive` captured its general purpose quite well. ## Stability and location As this is so simple, it can be in `core`. I feel that it can be stabilized quite soon after it is merged, if the libs teams feels its reasonable to add. Also, I don't really know how unstable feature work in std/core's codebases, so I might need help fixing them ## Tips for review The docs probably are the thing that needs to be reviewed! I tried my best, but I'm sure people have more experience than me writing docs for `Core` ### Implementation: The API is mostly pulled from https://docs.rs/sync_wrapper/latest/sync_wrapper/struct.SyncWrapper.html (which is apache 2.0 licenesed), and the implementation is trivial: - its an unsafe justification for pinning - its an unsafe justification for the `Sync` impl (mostly reasoned about by ```@danielhenrymantilla``` here: Actyx/sync_wrapper#2) - and forwarding impls, starting with derivable ones and `Future`
…askrgr Rollup of 10 pull requests Successful merges: - rust-lang#97629 ([core] add `Exclusive` to sync) - rust-lang#98503 (fix data race in thread::scope) - rust-lang#98670 (llvm-wrapper: adapt for LLVMConstExtractValue removal) - rust-lang#98671 (Fix source sidebar bugs) - rust-lang#98677 (For diagnostic information of Boolean, remind it as use the type: 'bool') - rust-lang#98684 (add test for 72793) - rust-lang#98688 (interpret: add From<&MplaceTy> for PlaceTy) - rust-lang#98695 (use "or pattern") - rust-lang#98709 (Remove unneeded methods declaration for old web browsers) - rust-lang#98717 (get rid of tidy 'unnecessarily ignored' warnings) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
(discussed here: https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Adding.20.60SyncWrapper.60.20to.20std)
Exclusive
is a wrapper that exclusively allows mutable access to the inner value if you have exclusive access to the wrapper. It acts like a compile time mutex, and hold an unconditionalSync
implementation.Justification for inclusion into std
futures::future::BoxFuture
's causing a central struct in a script to be non-Sync
. To work around it, you either write really difficult code, or wrap the futures in a needless mutex.Sync
implementation has very clear reasoning&/&mut
are toRwLock
asExclusive
is toMutex
Public Api
Naming
This is a big bikeshed, but I felt that
Exclusive
captured its general purpose quite well.Stability and location
As this is so simple, it can be in
core
. I feel that it can be stabilized quite soon after it is merged, if the libs teams feels its reasonable to add. Also, I don't really know how unstable feature work in std/core's codebases, so I might need help fixing themTips for review
The docs probably are the thing that needs to be reviewed! I tried my best, but I'm sure people have more experience than me writing docs for
Core
Implementation:
The API is mostly pulled from https://docs.rs/sync_wrapper/latest/sync_wrapper/struct.SyncWrapper.html (which is apache 2.0 licenesed), and the implementation is trivial:
Sync
impl (mostly reasoned about by @danielhenrymantilla here: Remove theT : Send
bound forSyncWrapper
to beSync
Actyx/sync_wrapper#2)Future