-
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
alloc: Add unstable Cfg feature `no_global_oom_handling #84266
alloc: Add unstable Cfg feature `no_global_oom_handling #84266
Conversation
(rust-highfive has picked a reviewer for you, use r? to override) |
This comment has been minimized.
This comment has been minimized.
I added the WIP because something in |
01d00b5
to
c7e5e77
Compare
This comment has been minimized.
This comment has been minimized.
Also not sure why that failure happened, because I don't see any |
|
This is a lot of Of course that'll require a lot of code motion now, but but it provides clearer separation and it'll be more obvious in the future when people add things in the wrong place. |
The test seems to use |
What happens if someone forgets to put |
It will fail with a compilation error as |
Indeed that is what's happening with right now. I should have those 2 things fixed in a bit. Thanks for the tips, everyone. |
c7e5e77
to
e32dc47
Compare
@the8472 It is a lot of |
The PR is passing, but it would be nice to have a test that the build with |
@@ -2,4 +2,4 @@ | |||
|
|||
all: | |||
$(RUSTC) fakealloc.rs | |||
$(RUSTC) --edition=2018 --crate-type=rlib ../../../../library/alloc/src/lib.rs --cfg feature=\"external_crate\" --extern external=$(TMPDIR)/$(shell $(RUSTC) --print file-names fakealloc.rs) | |||
$(RUSTC) --edition=2018 --crate-type=rlib ../../../../library/alloc/src/lib.rs --cfg feature=\"external_crate\" --cfg=feature=\"global-oom-handling\" --extern external=$(TMPDIR)/$(shell $(RUSTC) --print file-names fakealloc.rs) |
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.
This test doesn't seem to test anything anymore: https://github.com/rust-lang/rust/search?q=external_crate
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.
By the way I think you can use the same pattern to test liballoc without the global-oom-handling
feature.
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.
Agreed it does seem that way.
I'm not sure what you're concerned about. Potential churn in the long-term once those modules are established or the way to creating those modules? If it's about the latter then we can probably minimize the conflict potential by only creating a |
Churn in the short term why
When a |
I don't understand what you mean by that. The try methods have to be created either way. They're not just created for a short term reason and then removed later. So "churn in the short term" cannot be their raison d'être.
I don't see the difference here. You need to create a new method which is sortof but not quite copy-pasted from the old one. And then redirect the old one to the new one. This has to happen either way whether they live in different files or not and whether you annotate on the function level or not. And if necessary can be split into multiple commits to make it easier for git to follow the movement. E.g. you could implement the try method first in the base module and then once it is done use a separate commit to move it into the fallible module. Yes, in the short-term this means a little more work, but in the long-term it makes it clearer which part belongs to the restricted fallible API set and which to the more lax infallible APIs. |
Git blame doesn't track moves between files at all unless you pass it
When doing it within the same file, git will consider most lines of non-try function in the previous version to be part of the try function in the current version. Take for example 92bfcd2#diff-0e10470fe128e30b8710381cb322bfe266884aa8d969619a9920fc6970cdceffL406-R450: @@ -403,7 +405,9 @@ impl<T, A: Alloc> RawVec<T, A> {
/// # Aborts
///
/// Aborts on OOM
- pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) {
+ pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize)
+ -> Result<(), CollectionAllocErr> {
+
unsafe {
// NOTE: we don't early branch on ZSTs here because we want this
// to actually catch "asking for more than usize::MAX" in that case.
@@ -413,16 +417,15 @@ impl<T, A: Alloc> RawVec<T, A> {
// Don't actually need any more capacity.
// Wrapping in case they gave a bad `used_cap`.
if self.cap().wrapping_sub(used_cap) >= needed_extra_cap {
- return;
+ return Ok(());
}
// Nothing we can really do about these checks :(
- let new_cap = used_cap.checked_add(needed_extra_cap).expect("capacity overflow");
- let new_layout = match Layout::array::<T>(new_cap) {
- Some(layout) => layout,
- None => panic!("capacity overflow"),
- };
- alloc_guard(new_layout.size());
+ let new_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?;
+ let new_layout = Layout::array::<T>(new_cap).ok_or(CapacityOverflow)?;
+
+ alloc_guard(new_layout.size())?;
+
let res = match self.current_layout() {
Some(layout) => {
let old_ptr = self.ptr.as_ptr() as *mut u8;
@@ -430,26 +433,34 @@ impl<T, A: Alloc> RawVec<T, A> {
}
None => self.a.alloc(new_layout),
};
- let uniq = match res {
- Ok(ptr) => Unique::new_unchecked(ptr as *mut T),
- Err(e) => self.a.oom(e),
- };
- self.ptr = uniq;
+
+ self.ptr = Unique::new_unchecked(res? as *mut T);
self.cap = new_cap;
+
+ Ok(())
}
}
+ pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) {
+ match self.try_reserve_exact(used_cap, needed_extra_cap) {
+ Err(CapacityOverflow) => panic!("capacity overflow"),
+ Err(AllocErr(e)) => self.a.oom(e),
+ Ok(()) => { /* yay */ }
+ }
+ }
+ |
Thanks @bjorn3 for laying this out much more clearly than I would have. |
I assume you're using
Yes, hence do that in one commit, then do the motion in another so that git history browsing can follow it more easily. |
@the8472 I'm not against ever rearranging code --- quite in support in fact. But I imagine the Assuming I'm correct in saying there will be many commits adding |
Ok, that might work. But on the other hand the current approach also means people have to reason about fallibility on a per-method level. And if methods are added incrementally that would also keep church limited in impact, so moving wouldn't be that bad either? |
Important news! |
- The edition should be 2021 to avoid warnings. - The `external_crate` feature was removed in commit 45bf1ed ("rustc: Allow changing the default allocator"). Note that commit d620ae1 ("Auto merge of rust-lang#84266") removed the old test, but the new one introduced passed the `--cfg` like in the old one. Signed-off-by: Miguel Ojeda <[email protected]>
…test, r=Dylan-DPC Modernize `alloc-no-oom-handling` test - The edition should be 2021 to avoid warnings. - The `external_crate` feature was removed in commit 45bf1ed ("rustc: Allow changing the default allocator"). Note that commit d620ae1 ("Auto merge of rust-lang#84266") removed the old test, but the new one introduced passed the `--cfg` like in the old one. Signed-off-by: Miguel Ojeda <[email protected]> --- This is intended to align this test to the new `no_rc` and `no_sync` ones being added in rust-lang#89891, but it makes sense on its own too.
`alloc`: add unstable cfg features `no_rc` and `no_sync` In Rust for Linux we are using these to make `alloc` a bit more modular. See rust-lang#86048 and rust-lang#84266 for similar requests. Of course, the particular names are not important.
`alloc`: add unstable cfg features `no_rc` and `no_sync` In Rust for Linux we are using these to make `alloc` a bit more modular. See rust-lang#86048 and rust-lang#84266 for similar requests. Of course, the particular names are not important.
…hout infallible allocation methods As a part of the work for rust-lang#86942 the `no_global_oom_handling` cfg was added in rust-lang#84266, however enabling that cfg makes it difficult to use `Vec`: the `vec!` macro is missing and there is no way to insert or push new elements, nor to create a `Vec` from an iterator (without resorting to `unsafe` APIs to manually expand and write to the underlying buffer). This change adds `try_` equivalent methods for all methods in `Vec` that are disabled by `no_global_oom_handling` as well as a `try_vec!` as the equivalent of `vec!`. Performance and implementation notes: A goal of this change was to make sure to NOT regress the performance of the existing infallible methods - a naive approach would be to move the actual implementation into the fallible method, then call it from the infallible method and `unwrap()`, but this would add extra compare+branch instructions into the infallible methods. It would also be possible to simply duplicate the code for the fallible version - I did opt for this in a few cases, but where the code was larger and more complex this would lead to a maintenance nightmare. Instead, I moved the implementation into an `*_impl` method that was then specialized based on a new `VecError` trait and returned `Result<_, VecError>`, this trait also provided a pair of methods for raising errors. Never (`!`) was used as the infallible version of this trait (and always panics) and `TryReserveError` as the fallible version (and returns the correct error object). All these `VecError` method were marked with `#[inline]`, so after inlining the compiler could see for the infallible version always returns `Ok()` on success (and panics on error) thus the `?` operator or `unwrap()` call was a no-op, and so the same non-branching instructions as before are generated. I also added `try_from_iter` and `try_expand` methods for completeness, even though their infallible equivalents are trait implementations.
`alloc`: add unstable cfg features `no_rc` and `no_sync` In Rust for Linux we are using these to make `alloc` a bit more modular. See rust-lang/rust#86048 and rust-lang/rust#84266 for similar requests. Of course, the particular names are not important.
For certain sorts of systems, programming, it's deemed essential that
all allocation failures be explicitly handled where they occur. For
example, see Linus Torvald's opinion in 1. Merely not calling global
panic handlers, or always
try_reserving
first (for vectors), is notdeemed good enough, because the mere presence of the global OOM handlers
is burdens static analysis.
One option for these projects to use rust would just be to skip
alloc
,rolling their own allocation abstractions. But this would, in my
opinion be a real shame.
alloc
has a fewtry_*
methods already, andwe could easily have more. Features like custom allocator support also
demonstrate and existing to support diverse use-cases with the same
abstractions.
A natural way to add such a feature flag would a Cargo feature, but
there are currently uncertainties around how std library crate's Cargo
features may or not be stable, so to avoid any risk of stabilizing by
mistake we are going with a more low-level "raw cfg" token, which
cannot be interacted with via Cargo alone.
Note also that since there is no notion of "default cfg tokens" outside
of Cargo features, we have to invert the condition from
global_oom_handling
to tonot(no_global_oom_handling)
. This breaksthe monotonicity that would be important for a Cargo feature (i.e.
turning on more features should never break compatibility), but it
doesn't matter for raw cfg tokens which are not intended to be
"constraint solved" by Cargo or anything else.
To support this use-case we create a new feature, "global-oom-handling",
on by default, and put the global OOM handler infra and everything else
it that depends on it behind it. By default, nothing is changed, but
users concerned about global handling can make sure it is disabled, and
be confident that all OOM handling is local and explicit.
For this first iteration, non-flat collections are outright disabled.
Vec
andString
don't yet havetry_*
allocation methods, but arekept anyways since they can be oom-safely created "from parts", and we
hope to add those
try_
methods in the future.