Skip to content

Commit 1920ed9

Browse files
committed
Use a single static for all default slice Arcs.
Also adds debug_asserts in Drop for Weak/Arc that the shared static is not being "dropped"/"deallocated".
1 parent 5df616a commit 1920ed9

File tree

1 file changed

+48
-29
lines changed

1 file changed

+48
-29
lines changed

Diff for: alloc/src/sync.rs

+48-29
Original file line numberDiff line numberDiff line change
@@ -2468,6 +2468,15 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Arc<T, A> {
24682468
// [2]: (https://github.com/rust-lang/rust/pull/41714)
24692469
acquire!(self.inner().strong);
24702470

2471+
// Make sure we aren't trying to "drop" the shared static for empty slices
2472+
// used by Default::default.
2473+
debug_assert!(
2474+
!ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner),
2475+
"Arcs backed by a static should never be reach a strong count of 0. \
2476+
Likely decrement_strong_count or from_raw were called too many times.",
2477+
);
2478+
2479+
24712480
unsafe {
24722481
self.drop_slow();
24732482
}
@@ -3059,6 +3068,15 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Weak<T, A> {
30593068

30603069
if inner.weak.fetch_sub(1, Release) == 1 {
30613070
acquire!(inner.weak);
3071+
3072+
// Make sure we aren't trying to "deallocate" the shared static for empty slices
3073+
// used by Default::default.
3074+
debug_assert!(
3075+
!ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner),
3076+
"Arc/Weaks backed by a static should never be deallocated. \
3077+
Likely decrement_strong_count or from_raw were called too many times.",
3078+
);
3079+
30623080
unsafe {
30633081
self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr()))
30643082
}
@@ -3300,6 +3318,28 @@ impl<T: Default> Default for Arc<T> {
33003318
}
33013319
}
33023320

3321+
/// Struct to hold the static `ArcInner` used for empty `Arc<str/CStr/[T]>` as
3322+
/// returned by `Default::default`.
3323+
///
3324+
/// Layout notes:
3325+
/// * `repr(align(16))` so we can use it for `[T]` with `align_of::<T>() <= 16`.
3326+
/// * `repr(C)` so `inner` is at offset 0 (and thus guaranteed to actually be aligned to 16).
3327+
/// * `[u8; 1]` (to be initialized with 0) so it can be used for `Arc<CStr>`.
3328+
#[repr(C, align(16))]
3329+
struct SliceArcInnerForStatic {
3330+
inner: ArcInner<[u8; 1]>,
3331+
}
3332+
const MAX_STATIC_INNER_SLICE_ALIGNMENT: usize = 16;
3333+
3334+
static STATIC_INNER_SLICE: SliceArcInnerForStatic = SliceArcInnerForStatic {
3335+
inner: ArcInner {
3336+
strong: atomic::AtomicUsize::new(1),
3337+
weak: atomic::AtomicUsize::new(1),
3338+
data: [0],
3339+
},
3340+
};
3341+
3342+
33033343
#[cfg(not(no_global_oom_handling))]
33043344
#[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")]
33053345
impl Default for Arc<str> {
@@ -3324,12 +3364,7 @@ impl Default for Arc<core::ffi::CStr> {
33243364
#[inline]
33253365
fn default() -> Self {
33263366
use core::ffi::CStr;
3327-
static STATIC_INNER_CSTR: ArcInner<[u8; 1]> = ArcInner {
3328-
strong: atomic::AtomicUsize::new(1),
3329-
weak: atomic::AtomicUsize::new(1),
3330-
data: [0],
3331-
};
3332-
let inner: NonNull<ArcInner<[u8]>> = NonNull::from(&STATIC_INNER_CSTR);
3367+
let inner: NonNull<ArcInner<[u8]>> = NonNull::from(&STATIC_INNER_SLICE.inner);
33333368
let inner: NonNull<ArcInner<CStr>> = NonNull::new(inner.as_ptr() as *mut ArcInner<CStr>).unwrap();
33343369
// `this` semantically is the Arc "owned" by the static, so make sure not to drop it.
33353370
let this: mem::ManuallyDrop<Arc<CStr>> = unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) };
@@ -3345,31 +3380,15 @@ impl<T> Default for Arc<[T]> {
33453380
/// This may or may not share an allocation with other Arcs.
33463381
#[inline]
33473382
fn default() -> Self {
3348-
let alignment_of_t: usize = mem::align_of::<T>();
3349-
// We only make statics for the lowest five alignments.
3350-
// Alignments greater than that will use dynamic allocation.
3351-
macro_rules! use_static_inner_for_alignments {
3352-
($($alignment:literal),*) => {
3353-
$(if alignment_of_t == $alignment {
3354-
// Note: this must be in a new scope because static and type names are unhygenic.
3355-
#[repr(align($alignment))]
3356-
struct Aligned;
3357-
static ALIGNED_STATIC_INNER: ArcInner<Aligned> = ArcInner {
3358-
strong: atomic::AtomicUsize::new(1),
3359-
weak: atomic::AtomicUsize::new(1),
3360-
data: Aligned,
3361-
};
3362-
let inner: NonNull<ArcInner<Aligned>> = NonNull::from(&ALIGNED_STATIC_INNER);
3363-
let inner: NonNull<ArcInner<[T; 0]>> = inner.cast();
3364-
// `this` semantically is the Arc "owned" by the static, so make sure not to drop it.
3365-
let this: mem::ManuallyDrop<Arc<[T; 0]>> = unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) };
3366-
return (*this).clone();
3367-
})*
3368-
};
3383+
if mem::align_of::<T>() <= MAX_STATIC_INNER_SLICE_ALIGNMENT {
3384+
let inner: NonNull<ArcInner<[u8; 1]>> = NonNull::from(&STATIC_INNER_SLICE.inner);
3385+
let inner: NonNull<ArcInner<[T; 0]>> = inner.cast();
3386+
// `this` semantically is the Arc "owned" by the static, so make sure not to drop it.
3387+
let this: mem::ManuallyDrop<Arc<[T; 0]>> = unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) };
3388+
return (*this).clone();
33693389
}
3370-
use_static_inner_for_alignments!(1, 2, 4, 8, 16);
33713390

3372-
// If T's alignment is not one of the ones we have a static for, make a new unique allocation.
3391+
// If T's alignment is too large for the static, make a new unique allocation.
33733392
let arr: [T; 0] = [];
33743393
Arc::from(arr)
33753394
}

0 commit comments

Comments
 (0)