From 28cb2d7dfb155ccb204f4d9a60af0e3460e59f5f Mon Sep 17 00:00:00 2001 From: Zachary S Date: Fri, 10 May 2024 14:23:37 -0500 Subject: [PATCH 1/2] Add fn into_raw_with_allocator to Rc/Arc/Weak. --- library/alloc/src/rc.rs | 50 +++++++++++++++++++++++------ library/alloc/src/sync.rs | 67 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 10 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 45b205356758f..de7b36c922c83 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1356,6 +1356,33 @@ impl Rc { ptr } + /// Consumes the `Rc`, returning the wrapped pointer and allocator. + /// + /// To avoid a memory leak the pointer must be converted back to an `Rc` using + /// [`Rc::from_raw_in`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::rc::Rc; + /// use std::alloc::System; + /// + /// let x = Rc::new_in("hello".to_owned(), System); + /// let (ptr, alloc) = Rc::into_raw_with_allocator(x); + /// assert_eq!(unsafe { &*ptr }, "hello"); + /// let x = unsafe { Rc::from_raw_in(ptr, alloc) }; + /// assert_eq!(&*x, "hello"); + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(this); + let ptr = Self::as_ptr(&this); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(Self::allocator(&this)) }; + (ptr, alloc) + } + /// Provides a raw pointer to the data. /// /// The counts are not affected in any way and the `Rc` is not consumed. The pointer is valid @@ -2999,11 +3026,11 @@ impl Weak { result } - /// Consumes the `Weak` and turns it into a raw pointer. + /// Consumes the `Weak`, returning the wrapped pointer and allocator. /// /// This converts the weak pointer into a raw pointer, while still preserving the ownership of /// one weak reference (the weak count is not modified by this operation). It can be turned - /// back into the `Weak` with [`from_raw`]. + /// back into the `Weak` with [`from_raw_in`]. /// /// The same restrictions of accessing the target of the pointer as with /// [`as_ptr`] apply. @@ -3011,27 +3038,30 @@ impl Weak { /// # Examples /// /// ``` + /// #![feature(allocator_api)] /// use std::rc::{Rc, Weak}; + /// use std::alloc::System; /// - /// let strong = Rc::new("hello".to_owned()); + /// let strong = Rc::new_in("hello".to_owned(), System); /// let weak = Rc::downgrade(&strong); - /// let raw = weak.into_raw(); + /// let (raw, alloc) = weak.into_raw_with_allocator(); /// /// assert_eq!(1, Rc::weak_count(&strong)); /// assert_eq!("hello", unsafe { &*raw }); /// - /// drop(unsafe { Weak::from_raw(raw) }); + /// drop(unsafe { Weak::from_raw_in(raw, alloc) }); /// assert_eq!(0, Rc::weak_count(&strong)); /// ``` /// - /// [`from_raw`]: Weak::from_raw + /// [`from_raw_in`]: Weak::from_raw_in /// [`as_ptr`]: Weak::as_ptr #[inline] #[unstable(feature = "allocator_api", issue = "32838")] - pub fn into_raw_and_alloc(self) -> (*const T, A) { - let rc = mem::ManuallyDrop::new(self); - let result = rc.as_ptr(); - let alloc = unsafe { ptr::read(&rc.alloc) }; + pub fn into_raw_with_allocator(self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(self); + let result = this.as_ptr(); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(this.allocator()) }; (result, alloc) } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index a35c99849b343..0df94e9e2cae1 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1496,6 +1496,34 @@ impl Arc { ptr } + /// Consumes the `Arc`, returning the wrapped pointer and allocator. + /// + /// To avoid a memory leak the pointer must be converted back to an `Arc` using + /// [`Arc::from_raw_in`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let x = Arc::new_in("hello".to_owned(), System); + /// let (ptr, alloc) = Arc::into_raw_with_allocator(x); + /// assert_eq!(unsafe { &*ptr }, "hello"); + /// let x = unsafe { Arc::from_raw_in(ptr, alloc) }; + /// assert_eq!(&*x, "hello"); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(this); + let ptr = Self::as_ptr(&this); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(Self::allocator(&this)) }; + (ptr, alloc) + } + /// Provides a raw pointer to the data. /// /// The counts are not affected in any way and the `Arc` is not consumed. The pointer is valid for @@ -2740,6 +2768,45 @@ impl Weak { result } + /// Consumes the `Weak`, returning the wrapped pointer and allocator. + /// + /// This converts the weak pointer into a raw pointer, while still preserving the ownership of + /// one weak reference (the weak count is not modified by this operation). It can be turned + /// back into the `Weak` with [`from_raw_in`]. + /// + /// The same restrictions of accessing the target of the pointer as with + /// [`as_ptr`] apply. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// use std::sync::{Arc, Weak}; + /// use std::alloc::System; + /// + /// let strong = Arc::new_in("hello".to_owned(), System); + /// let weak = Arc::downgrade(&strong); + /// let (raw, alloc) = weak.into_raw_with_allocator(); + /// + /// assert_eq!(1, Arc::weak_count(&strong)); + /// assert_eq!("hello", unsafe { &*raw }); + /// + /// drop(unsafe { Weak::from_raw_in(raw, alloc) }); + /// assert_eq!(0, Arc::weak_count(&strong)); + /// ``` + /// + /// [`from_raw_in`]: Weak::from_raw_in + /// [`as_ptr`]: Weak::as_ptr + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn into_raw_with_allocator(self) -> (*const T, A) { + let this = mem::ManuallyDrop::new(self); + let result = this.as_ptr(); + // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + let alloc = unsafe { ptr::read(Self::allocator(&this)) }; + (result, alloc) + } + /// Converts a raw pointer previously created by [`into_raw`] back into `Weak` in the provided /// allocator. /// From c895f6e958ee34f6c52b612156e70465d18bd81a Mon Sep 17 00:00:00 2001 From: Zachary S Date: Thu, 16 May 2024 21:09:05 -0500 Subject: [PATCH 2/2] Access alloc field directly in Arc/Rc::into_raw_with_allocator. ... since fn allocator doesn't exist yet. --- library/alloc/src/rc.rs | 4 ++-- library/alloc/src/sync.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index de7b36c922c83..18fb1e24f2248 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1379,7 +1379,7 @@ impl Rc { let this = mem::ManuallyDrop::new(this); let ptr = Self::as_ptr(&this); // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped - let alloc = unsafe { ptr::read(Self::allocator(&this)) }; + let alloc = unsafe { ptr::read(&this.alloc) }; (ptr, alloc) } @@ -3061,7 +3061,7 @@ impl Weak { let this = mem::ManuallyDrop::new(self); let result = this.as_ptr(); // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped - let alloc = unsafe { ptr::read(this.allocator()) }; + let alloc = unsafe { ptr::read(&this.alloc) }; (result, alloc) } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 0df94e9e2cae1..d4b7be8762c53 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1520,7 +1520,7 @@ impl Arc { let this = mem::ManuallyDrop::new(this); let ptr = Self::as_ptr(&this); // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped - let alloc = unsafe { ptr::read(Self::allocator(&this)) }; + let alloc = unsafe { ptr::read(&this.alloc) }; (ptr, alloc) } @@ -2803,7 +2803,7 @@ impl Weak { let this = mem::ManuallyDrop::new(self); let result = this.as_ptr(); // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped - let alloc = unsafe { ptr::read(Self::allocator(&this)) }; + let alloc = unsafe { ptr::read(&this.alloc) }; (result, alloc) }