-
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 fallible Box, Arc, and Rc allocator APIs #80310
Changes from 6 commits
d116f48
dd2c6c3
973fa8e
bb15fa1
589aa8e
8f3cb7d
375e7c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -153,7 +153,7 @@ use core::pin::Pin; | |
use core::ptr::{self, Unique}; | ||
use core::task::{Context, Poll}; | ||
|
||
use crate::alloc::{handle_alloc_error, Allocator, Global, Layout}; | ||
use crate::alloc::{handle_alloc_error, AllocError, Allocator, Global, Layout}; | ||
use crate::borrow::Cow; | ||
use crate::raw_vec::RawVec; | ||
use crate::str::from_boxed_utf8_unchecked; | ||
|
@@ -241,6 +241,77 @@ impl<T> Box<T> { | |
pub fn pin(x: T) -> Pin<Box<T>> { | ||
(box x).into() | ||
} | ||
|
||
/// Allocates memory on the heap then places `x` into it, | ||
/// returning an error if the allocation fails | ||
/// | ||
/// This doesn't actually allocate if `T` is zero-sized. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(allocator_api)] | ||
/// | ||
/// let five = Box::try_new(5)?; | ||
/// # Ok::<(), std::alloc::AllocError>(()) | ||
/// ``` | ||
#[unstable(feature = "allocator_api", issue = "32838")] | ||
#[inline] | ||
pub fn try_new(x: T) -> Result<Self, AllocError> { | ||
Self::try_new_in(x, Global) | ||
} | ||
|
||
/// Constructs a new box with uninitialized contents on the heap, | ||
/// returning an error if the allocation fails | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(allocator_api, new_uninit)] | ||
/// | ||
/// | ||
/// let mut five = Box::<u32>::try_new_uninit()?; | ||
/// | ||
/// let five = unsafe { | ||
/// // Deferred initialization: | ||
/// five.as_mut_ptr().write(5); | ||
/// | ||
/// five.assume_init() | ||
/// }; | ||
/// | ||
/// assert_eq!(*five, 5); | ||
/// # Ok::<(), std::alloc::AllocError>(()) | ||
/// ``` | ||
#[unstable(feature = "allocator_api", issue = "32838")] | ||
// #[unstable(feature = "new_uninit", issue = "63291")] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be |
||
pub fn try_new_uninit() -> Result<Box<mem::MaybeUninit<T>>, AllocError> { | ||
Box::try_new_uninit_in(Global) | ||
} | ||
|
||
/// Constructs a new `Box` with uninitialized contents, with the memory | ||
/// being filled with `0` bytes on the heap | ||
/// | ||
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage | ||
/// of this method. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(allocator_api, new_uninit)] | ||
/// | ||
/// let zero = Box::<u32>::try_new_zeroed()?; | ||
/// let zero = unsafe { zero.assume_init() }; | ||
/// | ||
/// assert_eq!(*zero, 0); | ||
/// # Ok::<(), std::alloc::AllocError>(()) | ||
/// ``` | ||
/// | ||
/// [zeroed]: mem::MaybeUninit::zeroed | ||
#[unstable(feature = "allocator_api", issue = "32838")] | ||
// #[unstable(feature = "new_uninit", issue = "63291")] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be |
||
pub fn try_new_zeroed() -> Result<Box<mem::MaybeUninit<T>>, AllocError> { | ||
Box::try_new_zeroed_in(Global) | ||
} | ||
} | ||
|
||
impl<T, A: Allocator> Box<T, A> { | ||
|
@@ -267,6 +338,31 @@ impl<T, A: Allocator> Box<T, A> { | |
} | ||
} | ||
|
||
/// Allocates memory in the given allocator then places `x` into it, | ||
/// returning an error if the allocation fails | ||
/// | ||
/// This doesn't actually allocate if `T` is zero-sized. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(allocator_api)] | ||
/// | ||
/// use std::alloc::System; | ||
/// | ||
/// let five = Box::try_new_in(5, System)?; | ||
/// # Ok::<(), std::alloc::AllocError>(()) | ||
/// ``` | ||
#[unstable(feature = "allocator_api", issue = "32838")] | ||
#[inline] | ||
pub fn try_new_in(x: T, alloc: A) -> Result<Self, AllocError> { | ||
let mut boxed = Self::try_new_uninit_in(alloc)?; | ||
unsafe { | ||
boxed.as_mut_ptr().write(x); | ||
Ok(boxed.assume_init()) | ||
} | ||
} | ||
|
||
/// Constructs a new box with uninitialized contents in the provided allocator. | ||
/// | ||
/// # Examples | ||
|
@@ -291,8 +387,37 @@ impl<T, A: Allocator> Box<T, A> { | |
// #[unstable(feature = "new_uninit", issue = "63291")] | ||
pub fn new_uninit_in(alloc: A) -> Box<mem::MaybeUninit<T>, A> { | ||
let layout = Layout::new::<mem::MaybeUninit<T>>(); | ||
let ptr = alloc.allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)).cast(); | ||
unsafe { Box::from_raw_in(ptr.as_ptr(), alloc) } | ||
Box::try_new_uninit_in(alloc).unwrap_or_else(|_| handle_alloc_error(layout)) | ||
} | ||
|
||
/// Constructs a new box with uninitialized contents in the provided allocator, | ||
/// returning an error if the allocation fails | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(allocator_api, new_uninit)] | ||
/// | ||
/// use std::alloc::System; | ||
/// | ||
/// let mut five = Box::<u32, _>::try_new_uninit_in(System)?; | ||
/// | ||
/// let five = unsafe { | ||
/// // Deferred initialization: | ||
/// five.as_mut_ptr().write(5); | ||
/// | ||
/// five.assume_init() | ||
/// }; | ||
/// | ||
/// assert_eq!(*five, 5); | ||
/// # Ok::<(), std::alloc::AllocError>(()) | ||
/// ``` | ||
#[unstable(feature = "allocator_api", issue = "32838")] | ||
// #[unstable(feature = "new_uninit", issue = "63291")] | ||
pub fn try_new_uninit_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unclear if I should also be adding these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have no objections |
||
let layout = Layout::new::<mem::MaybeUninit<T>>(); | ||
let ptr = alloc.allocate(layout)?.cast(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did not update There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As the current implementation of |
||
unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) } | ||
} | ||
|
||
/// Constructs a new `Box` with uninitialized contents, with the memory | ||
|
@@ -319,9 +444,37 @@ impl<T, A: Allocator> Box<T, A> { | |
// #[unstable(feature = "new_uninit", issue = "63291")] | ||
pub fn new_zeroed_in(alloc: A) -> Box<mem::MaybeUninit<T>, A> { | ||
let layout = Layout::new::<mem::MaybeUninit<T>>(); | ||
let ptr = | ||
alloc.allocate_zeroed(layout).unwrap_or_else(|_| handle_alloc_error(layout)).cast(); | ||
unsafe { Box::from_raw_in(ptr.as_ptr(), alloc) } | ||
Box::try_new_zeroed_in(alloc).unwrap_or_else(|_| handle_alloc_error(layout)) | ||
} | ||
|
||
/// Constructs a new `Box` with uninitialized contents, with the memory | ||
/// being filled with `0` bytes in the provided allocator, | ||
/// returning an error if the allocation fails, | ||
/// | ||
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage | ||
/// of this method. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(allocator_api, new_uninit)] | ||
/// | ||
/// use std::alloc::System; | ||
/// | ||
/// let zero = Box::<u32, _>::try_new_zeroed_in(System)?; | ||
/// let zero = unsafe { zero.assume_init() }; | ||
/// | ||
/// assert_eq!(*zero, 0); | ||
/// # Ok::<(), std::alloc::AllocError>(()) | ||
/// ``` | ||
/// | ||
/// [zeroed]: mem::MaybeUninit::zeroed | ||
#[unstable(feature = "allocator_api", issue = "32838")] | ||
// #[unstable(feature = "new_uninit", issue = "63291")] | ||
pub fn try_new_zeroed_in(alloc: A) -> Result<Box<mem::MaybeUninit<T>, A>, AllocError> { | ||
let layout = Layout::new::<mem::MaybeUninit<T>>(); | ||
let ptr = alloc.allocate_zeroed(layout)?.cast(); | ||
unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) } | ||
} | ||
|
||
/// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement `Unpin`, then | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -453,6 +453,95 @@ impl<T> Rc<T> { | |
} | ||
} | ||
|
||
/// Constructs a new `Rc<T>`, returning an error if the allocation fails | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(allocator_api)] | ||
/// use std::rc::Rc; | ||
/// | ||
/// let five = Rc::try_new(5); | ||
/// # Ok::<(), std::alloc::AllocError>(()) | ||
/// ``` | ||
#[unstable(feature = "allocator_api", issue = "32838")] | ||
pub fn try_new(value: T) -> Result<Rc<T>, AllocError> { | ||
// There is an implicit weak pointer owned by all the strong | ||
// pointers, which ensures that the weak destructor never frees | ||
// the allocation while the strong destructor is running, even | ||
// if the weak pointer is stored inside the strong one. | ||
Ok(Self::from_inner( | ||
Box::leak(Box::try_new(RcBox { strong: Cell::new(1), weak: Cell::new(1), value })?) | ||
.into(), | ||
)) | ||
} | ||
|
||
/// Constructs a new `Rc` with uninitialized contents, returning an error if the allocation fails | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(allocator_api, new_uninit)] | ||
/// #![feature(get_mut_unchecked)] | ||
/// | ||
/// use std::rc::Rc; | ||
/// | ||
/// let mut five = Rc::<u32>::try_new_uninit()?; | ||
/// | ||
/// let five = unsafe { | ||
/// // Deferred initialization: | ||
/// Rc::get_mut_unchecked(&mut five).as_mut_ptr().write(5); | ||
/// | ||
/// five.assume_init() | ||
/// }; | ||
/// | ||
/// assert_eq!(*five, 5); | ||
/// # Ok::<(), std::alloc::AllocError>(()) | ||
/// ``` | ||
#[unstable(feature = "allocator_api", issue = "32838")] | ||
// #[unstable(feature = "new_uninit", issue = "63291")] | ||
pub fn try_new_uninit() -> Result<Rc<mem::MaybeUninit<T>>, AllocError> { | ||
unsafe { | ||
Ok(Rc::from_ptr(Rc::try_allocate_for_layout( | ||
Layout::new::<T>(), | ||
|layout| Global.allocate(layout), | ||
|mem| mem as *mut RcBox<mem::MaybeUninit<T>>, | ||
)?)) | ||
} | ||
} | ||
|
||
/// Constructs a new `Rc` with uninitialized contents, with the memory | ||
/// being filled with `0` bytes, returning an error if the allocation fails | ||
/// | ||
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and | ||
/// incorrect usage of this method. | ||
/// | ||
/// # Examples | ||
/// | ||
/// ``` | ||
/// #![feature(allocator_api, new_uninit)] | ||
/// | ||
/// use std::rc::Rc; | ||
/// | ||
/// let zero = Rc::<u32>::try_new_zeroed()?; | ||
/// let zero = unsafe { zero.assume_init() }; | ||
/// | ||
/// assert_eq!(*zero, 0); | ||
/// # Ok::<(), std::alloc::AllocError>(()) | ||
/// ``` | ||
/// | ||
/// [zeroed]: mem::MaybeUninit::zeroed | ||
#[unstable(feature = "allocator_api", issue = "32838")] | ||
//#[unstable(feature = "new_uninit", issue = "63291")] | ||
pub fn try_new_zeroed() -> Result<Rc<mem::MaybeUninit<T>>, AllocError> { | ||
unsafe { | ||
Ok(Rc::from_ptr(Rc::try_allocate_for_layout( | ||
Layout::new::<T>(), | ||
|layout| Global.allocate_zeroed(layout), | ||
|mem| mem as *mut RcBox<mem::MaybeUninit<T>>, | ||
)?)) | ||
} | ||
} | ||
/// Constructs a new `Pin<Rc<T>>`. If `T` does not implement `Unpin`, then | ||
/// `value` will be pinned in memory and unable to be moved. | ||
#[stable(feature = "pin", since = "1.33.0")] | ||
|
@@ -1018,9 +1107,32 @@ impl<T: ?Sized> Rc<T> { | |
// `&*(ptr as *const RcBox<T>)`, but this created a misaligned | ||
// reference (see #54908). | ||
let layout = Layout::new::<RcBox<()>>().extend(value_layout).unwrap().0.pad_to_align(); | ||
unsafe { | ||
Rc::try_allocate_for_layout(value_layout, allocate, mem_to_rcbox) | ||
.unwrap_or_else(|_| handle_alloc_error(layout)) | ||
} | ||
} | ||
|
||
/// Allocates an `RcBox<T>` with sufficient space for | ||
/// a possibly-unsized inner value where the value has the layout provided, | ||
/// returning an error if allocation fails. | ||
/// | ||
/// The function `mem_to_rcbox` is called with the data pointer | ||
/// and must return back a (potentially fat)-pointer for the `RcBox<T>`. | ||
#[inline] | ||
unsafe fn try_allocate_for_layout( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could you make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh ok. too bad the layout still needs to be recomputed. |
||
value_layout: Layout, | ||
allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>, | ||
mem_to_rcbox: impl FnOnce(*mut u8) -> *mut RcBox<T>, | ||
) -> Result<*mut RcBox<T>, AllocError> { | ||
// Calculate layout using the given value layout. | ||
// Previously, layout was calculated on the expression | ||
// `&*(ptr as *const RcBox<T>)`, but this created a misaligned | ||
// reference (see #54908). | ||
let layout = Layout::new::<RcBox<()>>().extend(value_layout).unwrap().0.pad_to_align(); | ||
|
||
// Allocate for the layout. | ||
let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)); | ||
let ptr = allocate(layout)?; | ||
|
||
// Initialize the RcBox | ||
let inner = mem_to_rcbox(ptr.as_non_null_ptr().as_ptr()); | ||
|
@@ -1031,7 +1143,7 @@ impl<T: ?Sized> Rc<T> { | |
ptr::write(&mut (*inner).weak, Cell::new(1)); | ||
} | ||
|
||
inner | ||
Ok(inner) | ||
} | ||
|
||
/// Allocates an `RcBox<T>` with sufficient space for an unsized inner value | ||
|
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.