about summary refs log tree commit diff
diff options
context:
space:
mode:
authorZachary S <zasample18+github@gmail.com>2024-05-11 09:54:32 -0500
committerZachary S <zasample18+github@gmail.com>2024-05-19 11:02:22 -0500
commite6396bca013b1b4cc1a177ef11e8fd5c270a4ea5 (patch)
tree0c50509da0235447462d3adee8b97d87b7c4248e
parent7d2a95b143272062b8a5722910f6e9945f131751 (diff)
downloadrust-e6396bca013b1b4cc1a177ef11e8fd5c270a4ea5.tar.gz
rust-e6396bca013b1b4cc1a177ef11e8fd5c270a4ea5.zip
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".
-rw-r--r--library/alloc/src/sync.rs77
1 files changed, 48 insertions, 29 deletions
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index a925b544bc2..9081ea5c679 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -2468,6 +2468,15 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Arc<T, A> {
         // [2]: (https://github.com/rust-lang/rust/pull/41714)
         acquire!(self.inner().strong);
 
+        // Make sure we aren't trying to "drop" the shared static for empty slices
+        // used by Default::default.
+        debug_assert!(
+            !ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner),
+            "Arcs backed by a static should never be reach a strong count of 0. \
+            Likely decrement_strong_count or from_raw were called too many times.",
+        );
+
+
         unsafe {
             self.drop_slow();
         }
@@ -3059,6 +3068,15 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Weak<T, A> {
 
         if inner.weak.fetch_sub(1, Release) == 1 {
             acquire!(inner.weak);
+
+            // Make sure we aren't trying to "deallocate" the shared static for empty slices
+            // used by Default::default.
+            debug_assert!(
+                !ptr::addr_eq(self.ptr.as_ptr(), &STATIC_INNER_SLICE.inner),
+                "Arc/Weaks backed by a static should never be deallocated. \
+                Likely decrement_strong_count or from_raw were called too many times.",
+            );
+
             unsafe {
                 self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr()))
             }
@@ -3300,6 +3318,28 @@ impl<T: Default> Default for Arc<T> {
     }
 }
 
+/// Struct to hold the static `ArcInner` used for empty `Arc<str/CStr/[T]>` as
+/// returned by `Default::default`.
+///
+/// Layout notes:
+/// * `repr(align(16))` so we can use it for `[T]` with `align_of::<T>() <= 16`.
+/// * `repr(C)` so `inner` is at offset 0 (and thus guaranteed to actually be aligned to 16).
+/// * `[u8; 1]` (to be initialized with 0) so it can be used for `Arc<CStr>`.
+#[repr(C, align(16))]
+struct SliceArcInnerForStatic {
+    inner: ArcInner<[u8; 1]>,
+}
+const MAX_STATIC_INNER_SLICE_ALIGNMENT: usize = 16;
+
+static STATIC_INNER_SLICE: SliceArcInnerForStatic = SliceArcInnerForStatic {
+    inner: ArcInner {
+        strong: atomic::AtomicUsize::new(1),
+        weak: atomic::AtomicUsize::new(1),
+        data: [0],
+    },
+};
+
+
 #[cfg(not(no_global_oom_handling))]
 #[stable(feature = "more_rc_default_impls", since = "CURRENT_RUSTC_VERSION")]
 impl Default for Arc<str> {
@@ -3324,12 +3364,7 @@ impl Default for Arc<core::ffi::CStr> {
     #[inline]
     fn default() -> Self {
         use core::ffi::CStr;
-        static STATIC_INNER_CSTR: ArcInner<[u8; 1]> = ArcInner {
-            strong: atomic::AtomicUsize::new(1),
-            weak: atomic::AtomicUsize::new(1),
-            data: [0],
-        };
-        let inner: NonNull<ArcInner<[u8]>> = NonNull::from(&STATIC_INNER_CSTR);
+        let inner: NonNull<ArcInner<[u8]>> = NonNull::from(&STATIC_INNER_SLICE.inner);
         let inner: NonNull<ArcInner<CStr>> = NonNull::new(inner.as_ptr() as *mut ArcInner<CStr>).unwrap();
         // `this` semantically is the Arc "owned" by the static, so make sure not to drop it.
         let this: mem::ManuallyDrop<Arc<CStr>> = unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) };
@@ -3345,31 +3380,15 @@ impl<T> Default for Arc<[T]> {
     /// This may or may not share an allocation with other Arcs.
     #[inline]
     fn default() -> Self {
-        let alignment_of_t: usize = mem::align_of::<T>();
-        // We only make statics for the lowest five alignments.
-        // Alignments greater than that will use dynamic allocation.
-        macro_rules! use_static_inner_for_alignments {
-            ($($alignment:literal),*) => {
-                $(if alignment_of_t == $alignment {
-                    // Note: this must be in a new scope because static and type names are unhygenic.
-                    #[repr(align($alignment))]
-                    struct Aligned;
-                    static ALIGNED_STATIC_INNER: ArcInner<Aligned> = ArcInner {
-                        strong: atomic::AtomicUsize::new(1),
-                        weak: atomic::AtomicUsize::new(1),
-                        data: Aligned,
-                    };
-                    let inner: NonNull<ArcInner<Aligned>> = NonNull::from(&ALIGNED_STATIC_INNER);
-                    let inner: NonNull<ArcInner<[T; 0]>> = inner.cast();
-                    // `this` semantically is the Arc "owned" by the static, so make sure not to drop it.
-                    let this: mem::ManuallyDrop<Arc<[T; 0]>> = unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) };
-                    return (*this).clone();
-                })*
-            };
+        if mem::align_of::<T>() <= MAX_STATIC_INNER_SLICE_ALIGNMENT {
+            let inner: NonNull<ArcInner<[u8; 1]>> = NonNull::from(&STATIC_INNER_SLICE.inner);
+            let inner: NonNull<ArcInner<[T; 0]>> = inner.cast();
+            // `this` semantically is the Arc "owned" by the static, so make sure not to drop it.
+            let this: mem::ManuallyDrop<Arc<[T; 0]>> = unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) };
+            return (*this).clone();
         }
-        use_static_inner_for_alignments!(1, 2, 4, 8, 16);
 
-        // If T's alignment is not one of the ones we have a static for, make a new unique allocation.
+        // If T's alignment is too large for the static, make a new unique allocation.
         let arr: [T; 0] = [];
         Arc::from(arr)
     }