Skip to content

Commit bb15fa1

Browse files
committed
Add fallible Arc APIs (Arc::try_new_*)
1 parent 973fa8e commit bb15fa1

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed

library/alloc/src/sync.rs

+122
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,97 @@ impl<T> Arc<T> {
478478
unsafe { Pin::new_unchecked(Arc::new(data)) }
479479
}
480480

481+
/// Constructs a new `Arc<T>`, returning an error if allocation fails.
482+
///
483+
/// # Examples
484+
///
485+
/// ```
486+
/// #![feature(allocator_api)]
487+
/// use std::sync::Arc;
488+
///
489+
/// let five = Arc::try_new(5)?;
490+
/// # Ok::<(), std::alloc::AllocError>(())
491+
/// ```
492+
#[unstable(feature = "allocator_api", issue = "32838")]
493+
#[inline]
494+
pub fn try_new(data: T) -> Result<Arc<T>, AllocError> {
495+
// Start the weak pointer count as 1 which is the weak pointer that's
496+
// held by all the strong pointers (kinda), see std/rc.rs for more info
497+
let x: Box<_> = Box::try_new(ArcInner {
498+
strong: atomic::AtomicUsize::new(1),
499+
weak: atomic::AtomicUsize::new(1),
500+
data,
501+
})?;
502+
Ok(Self::from_inner(Box::leak(x).into()))
503+
}
504+
505+
/// Constructs a new `Arc` with uninitialized contents, returning an error
506+
/// if allocation fails.
507+
///
508+
/// # Examples
509+
///
510+
/// ```
511+
/// #![feature(new_uninit, allocator_api)]
512+
/// #![feature(get_mut_unchecked)]
513+
///
514+
/// use std::sync::Arc;
515+
///
516+
/// let mut five = Arc::<u32>::try_new_uninit()?;
517+
///
518+
/// let five = unsafe {
519+
/// // Deferred initialization:
520+
/// Arc::get_mut_unchecked(&mut five).as_mut_ptr().write(5);
521+
///
522+
/// five.assume_init()
523+
/// };
524+
///
525+
/// assert_eq!(*five, 5);
526+
/// # Ok::<(), std::alloc::AllocError>(())
527+
/// ```
528+
#[unstable(feature = "allocator_api", issue = "32838")]
529+
// #[unstable(feature = "new_uninit", issue = "63291")]
530+
pub fn try_new_uninit() -> Result<Arc<mem::MaybeUninit<T>>, AllocError> {
531+
unsafe {
532+
Ok(Arc::from_ptr(Arc::try_allocate_for_layout(
533+
Layout::new::<T>(),
534+
|layout| Global.allocate(layout),
535+
|mem| mem as *mut ArcInner<mem::MaybeUninit<T>>,
536+
)?))
537+
}
538+
}
539+
540+
/// Constructs a new `Arc` with uninitialized contents, with the memory
541+
/// being filled with `0` bytes, returning an error if allocation fails.
542+
///
543+
/// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage
544+
/// of this method.
545+
///
546+
/// # Examples
547+
///
548+
/// ```
549+
/// #![feature(new_uninit, allocator_api)]
550+
///
551+
/// use std::sync::Arc;
552+
///
553+
/// let zero = Arc::<u32>::try_new_zeroed()?;
554+
/// let zero = unsafe { zero.assume_init() };
555+
///
556+
/// assert_eq!(*zero, 0);
557+
/// # Ok::<(), std::alloc::AllocError>(())
558+
/// ```
559+
///
560+
/// [zeroed]: ../../std/mem/union.MaybeUninit.html#method.zeroed
561+
#[unstable(feature = "allocator_api", issue = "32838")]
562+
// #[unstable(feature = "new_uninit", issue = "63291")]
563+
pub fn try_new_zeroed() -> Result<Arc<mem::MaybeUninit<T>>, AllocError> {
564+
unsafe {
565+
Ok(Arc::from_ptr(Arc::try_allocate_for_layout(
566+
Layout::new::<T>(),
567+
|layout| Global.allocate_zeroed(layout),
568+
|mem| mem as *mut ArcInner<mem::MaybeUninit<T>>,
569+
)?))
570+
}
571+
}
481572
/// Returns the inner value, if the `Arc` has exactly one strong reference.
482573
///
483574
/// Otherwise, an [`Err`] is returned with the same `Arc` that was
@@ -1009,6 +1100,37 @@ impl<T: ?Sized> Arc<T> {
10091100
inner
10101101
}
10111102

1103+
/// Allocates an `ArcInner<T>` with sufficient space for
1104+
/// a possibly-unsized inner value where the value has the layout provided,
1105+
/// returning an error if allocation fails.
1106+
///
1107+
/// The function `mem_to_arcinner` is called with the data pointer
1108+
/// and must return back a (potentially fat)-pointer for the `ArcInner<T>`.
1109+
unsafe fn try_allocate_for_layout(
1110+
value_layout: Layout,
1111+
allocate: impl FnOnce(Layout) -> Result<NonNull<[u8]>, AllocError>,
1112+
mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner<T>,
1113+
) -> Result<*mut ArcInner<T>, AllocError> {
1114+
// Calculate layout using the given value layout.
1115+
// Previously, layout was calculated on the expression
1116+
// `&*(ptr as *const ArcInner<T>)`, but this created a misaligned
1117+
// reference (see #54908).
1118+
let layout = Layout::new::<ArcInner<()>>().extend(value_layout).unwrap().0.pad_to_align();
1119+
1120+
let ptr = allocate(layout)?;
1121+
1122+
// Initialize the ArcInner
1123+
let inner = mem_to_arcinner(ptr.as_non_null_ptr().as_ptr());
1124+
debug_assert_eq!(unsafe { Layout::for_value(&*inner) }, layout);
1125+
1126+
unsafe {
1127+
ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1));
1128+
ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1));
1129+
}
1130+
1131+
Ok(inner)
1132+
}
1133+
10121134
/// Allocates an `ArcInner<T>` with sufficient space for an unsized inner value.
10131135
unsafe fn allocate_for_ptr(ptr: *const T) -> *mut ArcInner<T> {
10141136
// Allocate for the `ArcInner<T>` using the given value.

0 commit comments

Comments
 (0)