From f9d328d7ffa17ab78eaa62a2976033a7176886d9 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 12 May 2019 14:53:01 +0200 Subject: [PATCH 1/2] sync::Weak::{as,from,into}_raw Methods on the Weak to access it as raw pointer to the data. --- src/liballoc/sync.rs | 164 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 158 insertions(+), 6 deletions(-) diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 90c7859b3db9e..70865656c510e 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -13,7 +13,7 @@ use core::borrow; use core::fmt; use core::cmp::{self, Ordering}; use core::intrinsics::abort; -use core::mem::{self, align_of_val, size_of_val}; +use core::mem::{self, align_of, align_of_val, size_of_val}; use core::ops::{Deref, Receiver, CoerceUnsized, DispatchFromDyn}; use core::pin::Pin; use core::ptr::{self, NonNull}; @@ -397,11 +397,7 @@ impl Arc { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - // Align the unsized value to the end of the ArcInner. - // Because it is ?Sized, it will always be the last field in memory. - let align = align_of_val(&*ptr); - let layout = Layout::new::>(); - let offset = (layout.size() + layout.padding_needed_for(align)) as isize; + let offset = data_offset(ptr); // Reverse the offset to find the original ArcInner. let fake_ptr = ptr as *mut ArcInner; @@ -1071,6 +1067,144 @@ impl Weak { ptr: NonNull::new(usize::MAX as *mut ArcInner).expect("MAX is not 0"), } } + + /// Returns a raw pointer to the object `T` pointed to by this `Weak`. + /// + /// It is up to the caller to ensure that the object is still alive when accessing it through + /// the pointer. + /// + /// The pointer may be [`null`] or be dangling in case the object has already been destroyed. + /// + /// # Examples + /// + /// ``` + /// #![feature(weak_into_raw)] + /// + /// use std::sync::{Arc, Weak}; + /// use std::ptr; + /// + /// let strong = Arc::new(42); + /// let weak = Arc::downgrade(&strong); + /// // Both point to the same object + /// assert!(ptr::eq(&*strong, Weak::as_raw(&weak))); + /// // The strong here keeps it alive, so we can still access the object. + /// assert_eq!(42, unsafe { *Weak::as_raw(&weak) }); + /// + /// drop(strong); + /// // But not any more. We can do Weak::as_raw(&weak), but accessing the pointer would lead to + /// // undefined behaviour. + /// // assert_eq!(42, unsafe { *Weak::as_raw(&weak) }); + /// ``` + /// + /// [`null`]: ../../std/ptr/fn.null.html + #[unstable(feature = "weak_into_raw", issue = "60728")] + pub fn as_raw(this: &Self) -> *const T { + match this.inner() { + None => ptr::null(), + Some(inner) => { + let offset = data_offset_sized::(); + let ptr = inner as *const ArcInner; + // Note: while the pointer we create may already point to dropped value, the + // allocation still lives (it must hold the weak point as long as we are alive). + // Therefore, the offset is OK to do, it won't get out of the allocation. + let ptr = unsafe { (ptr as *const u8).offset(offset) }; + ptr as *const T + } + } + } + + /// Consumes the `Weak` and turns it into a raw pointer. + /// + /// This converts the weak pointer into a raw pointer, preserving the original weak count. It + /// can be turned back into the `Weak` with [`from_raw`]. + /// + /// The same restrictions of accessing the target of the pointer as with + /// [`as_raw`] apply. + /// + /// # Examples + /// + /// ``` + /// #![feature(weak_into_raw)] + /// + /// use std::sync::{Arc, Weak}; + /// + /// let strong = Arc::new(42); + /// let weak = Arc::downgrade(&strong); + /// let raw = Weak::into_raw(weak); + /// + /// assert_eq!(1, Arc::weak_count(&strong)); + /// assert_eq!(42, unsafe { *raw }); + /// + /// drop(unsafe { Weak::from_raw(raw) }); + /// assert_eq!(0, Arc::weak_count(&strong)); + /// ``` + /// + /// [`from_raw`]: struct.Weak.html#method.from_raw + /// [`as_raw`]: struct.Weak.html#method.as_raw + #[unstable(feature = "weak_into_raw", issue = "60728")] + pub fn into_raw(this: Self) -> *const T { + let result = Self::as_raw(&this); + mem::forget(this); + result + } + + /// Converts a raw pointer previously created by [`into_raw`] back into + /// `Weak`. + /// + /// This can be used to safely get a strong reference (by calling [`upgrade`] + /// later) or to deallocate the weak count by dropping the `Weak`. + /// + /// It takes ownership of one weak count. In case a [`null`] is passed, a dangling [`Weak`] is + /// returned. + /// + /// # Safety + /// + /// The pointer must represent one valid weak count. In other words, it must point to `T` which + /// is or *was* managed by an [`Arc`] and the weak count of that [`Arc`] must not have reached + /// 0. It is allowed for the strong count to be 0. + /// + /// # Examples + /// + /// ``` + /// #![feature(weak_into_raw)] + /// + /// use std::sync::{Arc, Weak}; + /// + /// let strong = Arc::new(42); + /// + /// let raw_1 = Weak::into_raw(Arc::downgrade(&strong)); + /// let raw_2 = Weak::into_raw(Arc::downgrade(&strong)); + /// + /// assert_eq!(2, Arc::weak_count(&strong)); + /// + /// assert_eq!(42, *Weak::upgrade(&unsafe { Weak::from_raw(raw_1) }).unwrap()); + /// assert_eq!(1, Arc::weak_count(&strong)); + /// + /// drop(strong); + /// + /// // Decrement the last weak count. + /// assert!(Weak::upgrade(&unsafe { Weak::from_raw(raw_2) }).is_none()); + /// ``` + /// + /// [`null`]: ../../std/ptr/fn.null.html + /// [`into_raw`]: struct.Weak.html#method.into_raw + /// [`upgrade`]: struct.Weak.html#method.upgrade + /// [`Weak`]: struct.Weak.html + /// [`Arc`]: struct.Arc.html + #[unstable(feature = "weak_into_raw", issue = "60728")] + pub unsafe fn from_raw(ptr: *const T) -> Self { + if ptr.is_null() { + Self::new() + } else { + // See Arc::from_raw for details + let offset = data_offset(ptr); + let fake_ptr = ptr as *mut ArcInner; + let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + Weak { + ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw"), + } + } + } } impl Weak { @@ -2150,3 +2284,21 @@ impl AsRef for Arc { #[stable(feature = "pin", since = "1.33.0")] impl Unpin for Arc { } + +/// Computes the offset of the data field within ArcInner. +unsafe fn data_offset(ptr: *const T) -> isize { + // Align the unsized value to the end of the ArcInner. + // Because it is ?Sized, it will always be the last field in memory. + let align = align_of_val(&*ptr); + let layout = Layout::new::>(); + (layout.size() + layout.padding_needed_for(align)) as isize +} + +/// Computes the offset of the data field within ArcInner. +/// +/// Unlike [`data_offset`], this doesn't need the pointer, but it works only on `T: Sized`. +fn data_offset_sized() -> isize { + let align = align_of::(); + let layout = Layout::new::>(); + (layout.size() + layout.padding_needed_for(align)) as isize +} From 4f1dcb34dffc26a23fa8c2cac69a31d7557fd173 Mon Sep 17 00:00:00 2001 From: Michal 'vorner' Vaner Date: Sun, 19 May 2019 12:46:37 +0200 Subject: [PATCH 2/2] rc::Weak::{as,from,into}_raw Methods on the Weak to access it as a raw pointer to the data. --- src/liballoc/rc.rs | 162 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 156 insertions(+), 6 deletions(-) diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 0dffb19476f3d..1f357a719bb43 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -239,7 +239,7 @@ use core::fmt; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; use core::marker::{self, Unpin, Unsize, PhantomData}; -use core::mem::{self, align_of_val, forget, size_of_val}; +use core::mem::{self, align_of, align_of_val, forget, size_of_val}; use core::ops::{Deref, Receiver, CoerceUnsized, DispatchFromDyn}; use core::pin::Pin; use core::ptr::{self, NonNull}; @@ -416,11 +416,7 @@ impl Rc { /// ``` #[stable(feature = "rc_raw", since = "1.17.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - // Align the unsized value to the end of the RcBox. - // Because it is ?Sized, it will always be the last field in memory. - let align = align_of_val(&*ptr); - let layout = Layout::new::>(); - let offset = (layout.size() + layout.padding_needed_for(align)) as isize; + let offset = data_offset(ptr); // Reverse the offset to find the original RcBox. let fake_ptr = ptr as *mut RcBox; @@ -1262,6 +1258,143 @@ impl Weak { ptr: NonNull::new(usize::MAX as *mut RcBox).expect("MAX is not 0"), } } + + /// Returns a raw pointer to the object `T` pointed to by this `Weak`. + /// + /// It is up to the caller to ensure that the object is still alive when accessing it through + /// the pointer. + /// + /// The pointer may be [`null`] or be dangling in case the object has already been destroyed. + /// + /// # Examples + /// + /// ``` + /// #![feature(weak_into_raw)] + /// + /// use std::rc::{Rc, Weak}; + /// use std::ptr; + /// + /// let strong = Rc::new(42); + /// let weak = Rc::downgrade(&strong); + /// // Both point to the same object + /// assert!(ptr::eq(&*strong, Weak::as_raw(&weak))); + /// // The strong here keeps it alive, so we can still access the object. + /// assert_eq!(42, unsafe { *Weak::as_raw(&weak) }); + /// + /// drop(strong); + /// // But not any more. We can do Weak::as_raw(&weak), but accessing the pointer would lead to + /// // undefined behaviour. + /// // assert_eq!(42, unsafe { *Weak::as_raw(&weak) }); + /// ``` + /// + /// [`null`]: ../../std/ptr/fn.null.html + #[unstable(feature = "weak_into_raw", issue = "60728")] + pub fn as_raw(this: &Self) -> *const T { + match this.inner() { + None => ptr::null(), + Some(inner) => { + let offset = data_offset_sized::(); + let ptr = inner as *const RcBox; + // Note: while the pointer we create may already point to dropped value, the + // allocation still lives (it must hold the weak point as long as we are alive). + // Therefore, the offset is OK to do, it won't get out of the allocation. + let ptr = unsafe { (ptr as *const u8).offset(offset) }; + ptr as *const T + } + } + } + + /// Consumes the `Weak` and turns it into a raw pointer. + /// + /// This converts the weak pointer into a raw pointer, preserving the original weak count. It + /// can be turned back into the `Weak` with [`from_raw`]. + /// + /// The same restrictions of accessing the target of the pointer as with + /// [`as_raw`] apply. + /// + /// # Examples + /// + /// ``` + /// #![feature(weak_into_raw)] + /// + /// use std::rc::{Rc, Weak}; + /// + /// let strong = Rc::new(42); + /// let weak = Rc::downgrade(&strong); + /// let raw = Weak::into_raw(weak); + /// + /// assert_eq!(1, Rc::weak_count(&strong)); + /// assert_eq!(42, unsafe { *raw }); + /// + /// drop(unsafe { Weak::from_raw(raw) }); + /// assert_eq!(0, Rc::weak_count(&strong)); + /// ``` + /// + /// [`from_raw`]: struct.Weak.html#method.from_raw + /// [`as_raw`]: struct.Weak.html#method.as_raw + #[unstable(feature = "weak_into_raw", issue = "60728")] + pub fn into_raw(this: Self) -> *const T { + let result = Self::as_raw(&this); + mem::forget(this); + result + } + + /// Converts a raw pointer previously created by [`into_raw`] back into `Weak`. + /// + /// This can be used to safely get a strong reference (by calling [`upgrade`] + /// later) or to deallocate the weak count by dropping the `Weak`. + /// + /// It takes ownership of one weak count. In case a [`null`] is passed, a dangling [`Weak`] is + /// returned. + /// + /// # Safety + /// + /// The pointer must represent one valid weak count. In other words, it must point to `T` which + /// is or *was* managed by an [`Rc`] and the weak count of that [`Rc`] must not have reached + /// 0. It is allowed for the strong count to be 0. + /// + /// # Examples + /// + /// ``` + /// #![feature(weak_into_raw)] + /// + /// use std::rc::{Rc, Weak}; + /// + /// let strong = Rc::new(42); + /// + /// let raw_1 = Weak::into_raw(Rc::downgrade(&strong)); + /// let raw_2 = Weak::into_raw(Rc::downgrade(&strong)); + /// + /// assert_eq!(2, Rc::weak_count(&strong)); + /// + /// assert_eq!(42, *Weak::upgrade(&unsafe { Weak::from_raw(raw_1) }).unwrap()); + /// assert_eq!(1, Rc::weak_count(&strong)); + /// + /// drop(strong); + /// + /// // Decrement the last weak count. + /// assert!(Weak::upgrade(&unsafe { Weak::from_raw(raw_2) }).is_none()); + /// ``` + /// + /// [`null`]: ../../std/ptr/fn.null.html + /// [`into_raw`]: struct.Weak.html#method.into_raw + /// [`upgrade`]: struct.Weak.html#method.upgrade + /// [`Rc`]: struct.Rc.html + /// [`Weak`]: struct.Weak.html + #[unstable(feature = "weak_into_raw", issue = "60728")] + pub unsafe fn from_raw(ptr: *const T) -> Self { + if ptr.is_null() { + Self::new() + } else { + // See Rc::from_raw for details + let offset = data_offset(ptr); + let fake_ptr = ptr as *mut RcBox; + let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + Weak { + ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw"), + } + } + } } pub(crate) fn is_dangling(ptr: NonNull) -> bool { @@ -2007,3 +2140,20 @@ impl AsRef for Rc { #[stable(feature = "pin", since = "1.33.0")] impl Unpin for Rc { } + +unsafe fn data_offset(ptr: *const T) -> isize { + // Align the unsized value to the end of the RcBox. + // Because it is ?Sized, it will always be the last field in memory. + let align = align_of_val(&*ptr); + let layout = Layout::new::>(); + (layout.size() + layout.padding_needed_for(align)) as isize +} + +/// Computes the offset of the data field within ArcInner. +/// +/// Unlike [`data_offset`], this doesn't need the pointer, but it works only on `T: Sized`. +fn data_offset_sized() -> isize { + let align = align_of::(); + let layout = Layout::new::>(); + (layout.size() + layout.padding_needed_for(align)) as isize +}