Skip to content

Commit 8923cab

Browse files
committed
Generalize {Rc,Arc}::make_mut() to unsized types.
This requires introducing a new internal type `RcUninit`, which can own an `RcBox<T>` without requiring it to be initialized, sized, or a slice.
1 parent 6471ce9 commit 8923cab

File tree

5 files changed

+231
-28
lines changed

5 files changed

+231
-28
lines changed

library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@
141141
#![feature(maybe_uninit_uninit_array_transpose)]
142142
#![feature(pattern)]
143143
#![feature(pointer_byte_offsets)]
144+
#![feature(ptr_from_ref)]
144145
#![feature(ptr_internals)]
145146
#![feature(ptr_metadata)]
146147
#![feature(ptr_sub_ptr)]

library/alloc/src/rc.rs

+98-14
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ use std::boxed::Box;
249249
use core::any::Any;
250250
use core::borrow;
251251
use core::cell::Cell;
252+
#[cfg(not(no_global_oom_handling))]
252253
use core::clone::CloneToUninit;
253254
use core::cmp::Ordering;
254255
use core::fmt;
@@ -270,7 +271,6 @@ use core::slice::from_raw_parts_mut;
270271

271272
#[cfg(not(no_global_oom_handling))]
272273
use crate::alloc::handle_alloc_error;
273-
#[cfg(not(no_global_oom_handling))]
274274
use crate::alloc::{AllocError, Allocator, Global, Layout};
275275
use crate::borrow::{Cow, ToOwned};
276276
#[cfg(not(no_global_oom_handling))]
@@ -1651,7 +1651,8 @@ impl<T: ?Sized, A: Allocator> Rc<T, A> {
16511651
}
16521652
}
16531653

1654-
impl<T: Clone, A: Allocator + Clone> Rc<T, A> {
1654+
#[cfg(not(no_global_oom_handling))]
1655+
impl<T: ?Sized + CloneToUninit, A: Allocator + Clone> Rc<T, A> {
16551656
/// Makes a mutable reference into the given `Rc`.
16561657
///
16571658
/// If there are other `Rc` pointers to the same allocation, then `make_mut` will
@@ -1702,31 +1703,50 @@ impl<T: Clone, A: Allocator + Clone> Rc<T, A> {
17021703
/// assert!(76 == *data);
17031704
/// assert!(weak.upgrade().is_none());
17041705
/// ```
1705-
#[cfg(not(no_global_oom_handling))]
17061706
#[inline]
17071707
#[stable(feature = "rc_unique", since = "1.4.0")]
17081708
pub fn make_mut(this: &mut Self) -> &mut T {
1709+
let size_of_val = size_of_val::<T>(&**this);
1710+
17091711
if Rc::strong_count(this) != 1 {
17101712
// Gotta clone the data, there are other Rcs.
1711-
// Pre-allocate memory to allow writing the cloned value directly.
1712-
let mut rc = Self::new_uninit_in(this.alloc.clone());
1713-
unsafe {
1714-
let data = Rc::get_mut_unchecked(&mut rc);
1715-
(**this).clone_to_uninit(data.as_mut_ptr());
1716-
*this = rc.assume_init();
1717-
}
1713+
1714+
let this_data_ref: &T = &**this;
1715+
// `in_progress` drops the allocation if we panic before finishing initializing it.
1716+
let mut in_progress: RcUninit<T, A> = RcUninit::new(this_data_ref, this.alloc.clone());
1717+
1718+
// Initialize with clone of this.
1719+
let initialized_clone = unsafe {
1720+
// Clone. If the clone panics, `in_progress` will be dropped and clean up.
1721+
this_data_ref.clone_to_uninit(in_progress.data_ptr());
1722+
// Cast type of pointer, now that it is initialized.
1723+
in_progress.into_rc()
1724+
};
1725+
1726+
// Replace `this` with newly constructed Rc.
1727+
*this = initialized_clone;
17181728
} else if Rc::weak_count(this) != 0 {
17191729
// Can just steal the data, all that's left is Weaks
1720-
let mut rc = Self::new_uninit_in(this.alloc.clone());
1730+
1731+
// We don't need panic-protection like the above branch does, but we might as well
1732+
// use the same mechanism.
1733+
let mut in_progress: RcUninit<T, A> = RcUninit::new(&**this, this.alloc.clone());
17211734
unsafe {
1722-
let data = Rc::get_mut_unchecked(&mut rc);
1723-
data.as_mut_ptr().copy_from_nonoverlapping(&**this, 1);
1735+
// Initialize `in_progress` with move of **this.
1736+
// We have to express this in terms of bytes because `T: ?Sized`; there is no
1737+
// operation that just copies a value based on its `size_of_val()`.
1738+
ptr::copy_nonoverlapping(
1739+
ptr::from_ref(&**this).cast::<u8>(),
1740+
in_progress.data_ptr().cast::<u8>(),
1741+
size_of_val,
1742+
);
17241743

17251744
this.inner().dec_strong();
17261745
// Remove implicit strong-weak ref (no need to craft a fake
17271746
// Weak here -- we know other Weaks can clean up for us)
17281747
this.inner().dec_weak();
1729-
ptr::write(this, rc.assume_init());
1748+
// Replace `this` with newly constructed Rc that has the moved data.
1749+
ptr::write(this, in_progress.into_rc());
17301750
}
17311751
}
17321752
// This unsafety is ok because we're guaranteed that the pointer
@@ -1736,7 +1756,9 @@ impl<T: Clone, A: Allocator + Clone> Rc<T, A> {
17361756
// reference to the allocation.
17371757
unsafe { &mut this.ptr.as_mut().value }
17381758
}
1759+
}
17391760

1761+
impl<T: Clone, A: Allocator + Clone> Rc<T, A> {
17401762
/// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the
17411763
/// clone.
17421764
///
@@ -3360,6 +3382,68 @@ fn data_offset_align(align: usize) -> usize {
33603382
layout.size() + layout.padding_needed_for(align)
33613383
}
33623384

3385+
/// A unique owning pointer to a [`RcBox`] **that does not imply the contents are initialized,**
3386+
/// but will deallocate it (without dropping the value) when dropped.
3387+
///
3388+
/// This is a helper for [`Rc::make_mut()`] to ensure correct cleanup on panic.
3389+
#[cfg(not(no_global_oom_handling))]
3390+
struct RcUninit<T: ?Sized, A: Allocator> {
3391+
ptr: NonNull<RcBox<T>>,
3392+
layout_for_value: Layout,
3393+
alloc: Option<A>,
3394+
}
3395+
3396+
#[cfg(not(no_global_oom_handling))]
3397+
impl<T: ?Sized, A: Allocator> RcUninit<T, A> {
3398+
/// Allocate a RcBox with layout suitable to contain `for_value` or a clone of it.
3399+
fn new(for_value: &T, alloc: A) -> RcUninit<T, A> {
3400+
let layout = Layout::for_value(for_value);
3401+
let ptr = unsafe {
3402+
Rc::allocate_for_layout(
3403+
layout,
3404+
|layout_for_rcbox| alloc.allocate(layout_for_rcbox),
3405+
|mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const RcBox<T>),
3406+
)
3407+
};
3408+
Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) }
3409+
}
3410+
3411+
/// Returns the pointer to be written into to initialize the [`Rc`].
3412+
fn data_ptr(&mut self) -> *mut T {
3413+
let offset = data_offset_align(self.layout_for_value.align());
3414+
unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T }
3415+
}
3416+
3417+
/// Upgrade this into a normal [`Rc`].
3418+
///
3419+
/// # Safety
3420+
///
3421+
/// The data must have been initialized (by writing to [`Self::data_ptr()`]).
3422+
unsafe fn into_rc(mut self) -> Rc<T, A> {
3423+
let ptr = self.ptr;
3424+
let alloc = self.alloc.take().unwrap();
3425+
mem::forget(self);
3426+
// SAFETY: The pointer is valid as per `RcUninit::new`, and the caller is responsible
3427+
// for having initialized the data.
3428+
unsafe { Rc::from_ptr_in(ptr.as_ptr(), alloc) }
3429+
}
3430+
}
3431+
3432+
#[cfg(not(no_global_oom_handling))]
3433+
impl<T: ?Sized, A: Allocator> Drop for RcUninit<T, A> {
3434+
fn drop(&mut self) {
3435+
// SAFETY:
3436+
// * new() produced a pointer safe to deallocate.
3437+
// * We own the pointer unless into_rc() was called, which forgets us.
3438+
unsafe {
3439+
self.alloc
3440+
.take()
3441+
.unwrap()
3442+
.deallocate(self.ptr.cast(), rcbox_layout_for_value_layout(self.layout_for_value));
3443+
}
3444+
}
3445+
}
3446+
33633447
/// A uniquely owned `Rc`
33643448
///
33653449
/// This represents an `Rc` that is known to be uniquely owned -- that is, have exactly one strong

library/alloc/src/rc/tests.rs

+18
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,24 @@ fn test_cowrc_clone_weak() {
321321
assert!(cow1_weak.upgrade().is_none());
322322
}
323323

324+
/// This is similar to the doc-test for `Rc::make_mut()`, but on an unsized type (slice).
325+
#[test]
326+
fn test_cowrc_unsized() {
327+
use std::rc::Rc;
328+
329+
let mut data: Rc<[i32]> = Rc::new([10, 20, 30]);
330+
331+
Rc::make_mut(&mut data)[0] += 1; // Won't clone anything
332+
let mut other_data = Rc::clone(&data); // Won't clone inner data
333+
Rc::make_mut(&mut data)[1] += 1; // Clones inner data
334+
Rc::make_mut(&mut data)[2] += 1; // Won't clone anything
335+
Rc::make_mut(&mut other_data)[0] *= 10; // Won't clone anything
336+
337+
// Now `data` and `other_data` point to different allocations.
338+
assert_eq!(*data, [11, 21, 31]);
339+
assert_eq!(*other_data, [110, 20, 30]);
340+
}
341+
324342
#[test]
325343
fn test_show() {
326344
let foo = Rc::new(75);

library/alloc/src/sync.rs

+96-14
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
use core::any::Any;
1212
use core::borrow;
13+
#[cfg(not(no_global_oom_handling))]
1314
use core::clone::CloneToUninit;
1415
use core::cmp::Ordering;
1516
use core::fmt;
@@ -33,7 +34,6 @@ use core::sync::atomic::Ordering::{Acquire, Relaxed, Release};
3334

3435
#[cfg(not(no_global_oom_handling))]
3536
use crate::alloc::handle_alloc_error;
36-
#[cfg(not(no_global_oom_handling))]
3737
use crate::alloc::{AllocError, Allocator, Global, Layout};
3838
use crate::borrow::{Cow, ToOwned};
3939
use crate::boxed::Box;
@@ -2055,7 +2055,8 @@ impl<T: ?Sized, A: Allocator> Deref for Arc<T, A> {
20552055
#[unstable(feature = "receiver_trait", issue = "none")]
20562056
impl<T: ?Sized> Receiver for Arc<T> {}
20572057

2058-
impl<T: Clone, A: Allocator + Clone> Arc<T, A> {
2058+
#[cfg(not(no_global_oom_handling))]
2059+
impl<T: ?Sized + CloneToUninit, A: Allocator + Clone> Arc<T, A> {
20592060
/// Makes a mutable reference into the given `Arc`.
20602061
///
20612062
/// If there are other `Arc` pointers to the same allocation, then `make_mut` will
@@ -2106,10 +2107,11 @@ impl<T: Clone, A: Allocator + Clone> Arc<T, A> {
21062107
/// assert!(76 == *data);
21072108
/// assert!(weak.upgrade().is_none());
21082109
/// ```
2109-
#[cfg(not(no_global_oom_handling))]
21102110
#[inline]
21112111
#[stable(feature = "arc_unique", since = "1.4.0")]
21122112
pub fn make_mut(this: &mut Self) -> &mut T {
2113+
let size_of_val = mem::size_of_val::<T>(&**this);
2114+
21132115
// Note that we hold both a strong reference and a weak reference.
21142116
// Thus, releasing our strong reference only will not, by itself, cause
21152117
// the memory to be deallocated.
@@ -2120,13 +2122,19 @@ impl<T: Clone, A: Allocator + Clone> Arc<T, A> {
21202122
// deallocated.
21212123
if this.inner().strong.compare_exchange(1, 0, Acquire, Relaxed).is_err() {
21222124
// Another strong pointer exists, so we must clone.
2123-
// Pre-allocate memory to allow writing the cloned value directly.
2124-
let mut arc = Self::new_uninit_in(this.alloc.clone());
2125-
unsafe {
2126-
let data = Arc::get_mut_unchecked(&mut arc);
2127-
(**this).clone_to_uninit(data.as_mut_ptr());
2128-
*this = arc.assume_init();
2129-
}
2125+
2126+
let this_data_ref: &T = &**this;
2127+
// `in_progress` drops the allocation if we panic before finishing initializing it.
2128+
let mut in_progress: ArcUninit<T, A> =
2129+
ArcUninit::new(this_data_ref, this.alloc.clone());
2130+
2131+
let initialized_clone = unsafe {
2132+
// Clone. If the clone panics, `in_progress` will be dropped and clean up.
2133+
this_data_ref.clone_to_uninit(in_progress.data_ptr());
2134+
// Cast type of pointer, now that it is initialized.
2135+
in_progress.into_arc()
2136+
};
2137+
*this = initialized_clone;
21302138
} else if this.inner().weak.load(Relaxed) != 1 {
21312139
// Relaxed suffices in the above because this is fundamentally an
21322140
// optimization: we are always racing with weak pointers being
@@ -2145,11 +2153,21 @@ impl<T: Clone, A: Allocator + Clone> Arc<T, A> {
21452153
let _weak = Weak { ptr: this.ptr, alloc: this.alloc.clone() };
21462154

21472155
// Can just steal the data, all that's left is Weaks
2148-
let mut arc = Self::new_uninit_in(this.alloc.clone());
2156+
//
2157+
// We don't need panic-protection like the above branch does, but we might as well
2158+
// use the same mechanism.
2159+
let mut in_progress: ArcUninit<T, A> = ArcUninit::new(&**this, this.alloc.clone());
21492160
unsafe {
2150-
let data = Arc::get_mut_unchecked(&mut arc);
2151-
data.as_mut_ptr().copy_from_nonoverlapping(&**this, 1);
2152-
ptr::write(this, arc.assume_init());
2161+
// Initialize `in_progress` with move of **this.
2162+
// We have to express this in terms of bytes because `T: ?Sized`; there is no
2163+
// operation that just copies a value based on its `size_of_val()`.
2164+
ptr::copy_nonoverlapping(
2165+
ptr::from_ref(&**this).cast::<u8>(),
2166+
in_progress.data_ptr().cast::<u8>(),
2167+
size_of_val,
2168+
);
2169+
2170+
ptr::write(this, in_progress.into_arc());
21532171
}
21542172
} else {
21552173
// We were the sole reference of either kind; bump back up the
@@ -2161,7 +2179,9 @@ impl<T: Clone, A: Allocator + Clone> Arc<T, A> {
21612179
// either unique to begin with, or became one upon cloning the contents.
21622180
unsafe { Self::get_mut_unchecked(this) }
21632181
}
2182+
}
21642183

2184+
impl<T: Clone, A: Allocator + Clone> Arc<T, A> {
21652185
/// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the
21662186
/// clone.
21672187
///
@@ -3557,6 +3577,68 @@ fn data_offset_align(align: usize) -> usize {
35573577
layout.size() + layout.padding_needed_for(align)
35583578
}
35593579

3580+
/// A unique owning pointer to a [`ArcInner`] **that does not imply the contents are initialized,**
3581+
/// but will deallocate it (without dropping the value) when dropped.
3582+
///
3583+
/// This is a helper for [`Arc::make_mut()`] to ensure correct cleanup on panic.
3584+
#[cfg(not(no_global_oom_handling))]
3585+
struct ArcUninit<T: ?Sized, A: Allocator> {
3586+
ptr: NonNull<ArcInner<T>>,
3587+
layout_for_value: Layout,
3588+
alloc: Option<A>,
3589+
}
3590+
3591+
#[cfg(not(no_global_oom_handling))]
3592+
impl<T: ?Sized, A: Allocator> ArcUninit<T, A> {
3593+
/// Allocate a ArcInner with layout suitable to contain `for_value` or a clone of it.
3594+
fn new(for_value: &T, alloc: A) -> ArcUninit<T, A> {
3595+
let layout = Layout::for_value(for_value);
3596+
let ptr = unsafe {
3597+
Arc::allocate_for_layout(
3598+
layout,
3599+
|layout_for_arcinner| alloc.allocate(layout_for_arcinner),
3600+
|mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const ArcInner<T>),
3601+
)
3602+
};
3603+
Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) }
3604+
}
3605+
3606+
/// Returns the pointer to be written into to initialize the [`Arc`].
3607+
fn data_ptr(&mut self) -> *mut T {
3608+
let offset = data_offset_align(self.layout_for_value.align());
3609+
unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T }
3610+
}
3611+
3612+
/// Upgrade this into a normal [`Arc`].
3613+
///
3614+
/// # Safety
3615+
///
3616+
/// The data must have been initialized (by writing to [`Self::data_ptr()`]).
3617+
unsafe fn into_arc(mut self) -> Arc<T, A> {
3618+
let ptr = self.ptr;
3619+
let alloc = self.alloc.take().unwrap();
3620+
mem::forget(self);
3621+
// SAFETY: The pointer is valid as per `ArcUninit::new`, and the caller is responsible
3622+
// for having initialized the data.
3623+
unsafe { Arc::from_ptr_in(ptr.as_ptr(), alloc) }
3624+
}
3625+
}
3626+
3627+
#[cfg(not(no_global_oom_handling))]
3628+
impl<T: ?Sized, A: Allocator> Drop for ArcUninit<T, A> {
3629+
fn drop(&mut self) {
3630+
// SAFETY:
3631+
// * new() produced a pointer safe to deallocate.
3632+
// * We own the pointer unless into_arc() was called, which forgets us.
3633+
unsafe {
3634+
self.alloc.take().unwrap().deallocate(
3635+
self.ptr.cast(),
3636+
arcinner_layout_for_value_layout(self.layout_for_value),
3637+
);
3638+
}
3639+
}
3640+
}
3641+
35603642
#[stable(feature = "arc_error", since = "1.52.0")]
35613643
impl<T: core::error::Error + ?Sized> core::error::Error for Arc<T> {
35623644
#[allow(deprecated, deprecated_in_future)]

library/alloc/tests/arc.rs

+18
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,21 @@ fn weak_may_dangle() {
210210
// `val` dropped here while still borrowed
211211
// borrow might be used here, when `val` is dropped and runs the `Drop` code for type `std::sync::Weak`
212212
}
213+
214+
/// This is similar to the doc-test for `Arc::make_mut()`, but on an unsized type (slice).
215+
#[test]
216+
fn make_mut_unsized() {
217+
use alloc::sync::Arc;
218+
219+
let mut data: Arc<[i32]> = Arc::new([10, 20, 30]);
220+
221+
Arc::make_mut(&mut data)[0] += 1; // Won't clone anything
222+
let mut other_data = Arc::clone(&data); // Won't clone inner data
223+
Arc::make_mut(&mut data)[1] += 1; // Clones inner data
224+
Arc::make_mut(&mut data)[2] += 1; // Won't clone anything
225+
Arc::make_mut(&mut other_data)[0] *= 10; // Won't clone anything
226+
227+
// Now `data` and `other_data` point to different allocations.
228+
assert_eq!(*data, [11, 21, 31]);
229+
assert_eq!(*other_data, [110, 20, 30]);
230+
}

0 commit comments

Comments
 (0)