Skip to content

Commit

Permalink
Remove sized requirement from Arc::from_raw
Browse files Browse the repository at this point in the history
`ptr.byte_sub` is stable since 1.75.0.

The tricky part here is figuring out offset of `ArcInner.data` for
DST.  This PR assumes that raw pointer is coming from existing `Arc`
so it is safe to convert to reference. I'm not 100% sure this is
always correct.

Correct version would require `Layout::for_value_raw` (or
`mem::align_of_val_raw`) which is not stable.

This can be used to convert `Arc<String>` to `Arc<dyn Debug>`.
  • Loading branch information
stepancheg committed Apr 18, 2024
1 parent 0db2a24 commit 1799e98
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 53 deletions.
93 changes: 64 additions & 29 deletions src/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ pub(crate) struct ArcInner<T: ?Sized> {
unsafe impl<T: ?Sized + Sync + Send> Send for ArcInner<T> {}
unsafe impl<T: ?Sized + Sync + Send> Sync for ArcInner<T> {}

impl<T: ?Sized> ArcInner<T> {
/// Compute the offset of the `data` field within `ArcInner<T>`.
///
/// # Safety
///
/// - The pointer must be created from `Arc::into_raw` or similar functions
/// - The pointee must be initialized (`&*value` must not be UB)
unsafe fn offset_of_data(value: *const T) -> usize {
// We can use `Layout::for_value_raw` when it is stable.
let value = &*value;

let layout = Layout::new::<atomic::AtomicUsize>();
let (_, offset) = layout.extend(Layout::for_value(value)).unwrap();
offset
}
}

/// An atomically reference counted shared pointer
///
/// See the documentation for [`Arc`] in the standard library. Unlike the
Expand Down Expand Up @@ -71,26 +88,6 @@ impl<T> Arc<T> {
}
}

/// Reconstruct the `Arc<T>` from a raw pointer obtained from into_raw()
///
/// Note: This raw pointer will be offset in the allocation and must be preceded
/// by the atomic count.
///
/// It is recommended to use OffsetArc for this
///
/// # Safety
/// - The given pointer must be a valid pointer to `T` that came from [`Arc::into_raw`].
/// - After `from_raw`, the pointer must not be accessed.
#[inline]
pub unsafe fn from_raw(ptr: *const T) -> Self {
// FIXME: when `byte_sub` is stabilized, this can accept T: ?Sized.

// To find the corresponding pointer to the `ArcInner` we need
// to subtract the offset of the `data` field from the pointer.
let ptr = (ptr as *const u8).sub(offset_of!(ArcInner<T>, data));
Arc::from_raw_inner(ptr as *mut ArcInner<T>)
}

/// Temporarily converts |self| into a bonafide OffsetArc and exposes it to the
/// provided callback. The refcount is not modified.
#[inline(always)]
Expand Down Expand Up @@ -160,15 +157,7 @@ impl<T> Arc<[T]> {
/// - The given pointer must be a valid pointer to `[T]` that came from [`Arc::into_raw`].
/// - After `from_raw_slice`, the pointer must not be accessed.
pub unsafe fn from_raw_slice(ptr: *const [T]) -> Self {
let len = (*ptr).len();
// Assuming the offset of `T` in `ArcInner<T>` is the same
// as as offset of `[T]` in `ArcInner<[T]>`.
// (`offset_of!` macro requires `Sized`.)
let arc_inner_ptr = (ptr as *const u8).sub(offset_of!(ArcInner<T>, data));
// Synthesize the fat pointer: the pointer metadata for `Arc<[T]>`
// is the same as the pointer metadata for `[T]`: the length.
let fake_slice = ptr::slice_from_raw_parts_mut(arc_inner_ptr as *mut T, len);
Arc::from_raw_inner(fake_slice as *mut ArcInner<[T]>)
Arc::from_raw(ptr)
}
}

Expand All @@ -184,6 +173,33 @@ impl<T: ?Sized> Arc<T> {
this.as_ptr()
}

/// Reconstruct the `Arc<T>` from a raw pointer obtained from into_raw()
///
/// Note: This raw pointer will be offset in the allocation and must be preceded
/// by the atomic count.
///
/// It is recommended to use OffsetArc for this
///
/// # Safety
/// - The given pointer must be a valid pointer to `T` that came from [`Arc::into_raw`].
/// - After `from_raw`, the pointer must not be accessed.
#[inline]
pub unsafe fn from_raw(ptr: *const T) -> Self {
// To find the corresponding pointer to the `ArcInner` we need
// to subtract the offset of the `data` field from the pointer.

// SAFETY: `ptr` comes from `Arc`, so it must be initialized.
let offset_of_data = ArcInner::<T>::offset_of_data(ptr);

// SAFETY: if `ptr` comes from `Arc`, there's no overflow here,
// and resulting pointer is within the allocation.
let arc_inner_ptr = ptr.byte_sub(offset_of_data);

// SAFETY: `Arc::from_raw` contract requires that `ptr` comes from `Arc::into_raw`,
// so here we reconstruct the `Arc` back.
Arc::from_raw_inner(arc_inner_ptr as *mut ArcInner<T>)
}

/// Returns the raw pointer.
///
/// Same as into_raw except `self` isn't consumed.
Expand Down Expand Up @@ -1050,6 +1066,25 @@ mod tests {
assert_eq!(1, Arc::strong_count(&arc2));
}

#[test]
fn test_into_raw_from_raw_dst() {
trait AnInteger {
fn get_me_an_integer(&self) -> u64;
}

impl AnInteger for u32 {
fn get_me_an_integer(&self) -> u64 {
*self as u64
}
}

let arc = Arc::<u32>::new(19);
let data = Arc::into_raw(arc);
let data: *const dyn AnInteger = data as *const _;
let arc: Arc<dyn AnInteger> = unsafe { Arc::from_raw(data) };
assert_eq!(19, arc.get_me_an_integer());
}

#[allow(dead_code)]
const fn is_partial_ord<T: ?Sized + PartialOrd>() {}

Expand Down
24 changes: 0 additions & 24 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,30 +34,6 @@ extern crate stable_deref_trait;
#[cfg(feature = "unsize")]
extern crate unsize;

/// Calculates the offset of the specified field from the start of the named struct.
/// This macro is impossible to be const until feature(const_ptr_offset_from) is stable.
macro_rules! offset_of {
($ty: path, $field: tt) => {{
// ensure the type is a named struct
// ensure the field exists and is accessible
let $ty { $field: _, .. };

let uninit = <::core::mem::MaybeUninit<$ty>>::uninit(); // const since 1.36

let base_ptr: *const $ty = uninit.as_ptr(); // const since 1.59

#[allow(unused_unsafe)]
let field_ptr = unsafe { ::core::ptr::addr_of!((*base_ptr).$field) }; // since 1.51

// // the const version requires feature(const_ptr_offset_from)
// // https://github.com/rust-lang/rust/issues/92980
// #[allow(unused_unsafe)]
// unsafe { (field_ptr as *const u8).offset_from(base_ptr as *const u8) as usize }

(field_ptr as usize) - (base_ptr as usize)
}};
}

mod arc;
mod arc_borrow;
#[cfg(feature = "arc-swap")]
Expand Down

0 comments on commit 1799e98

Please sign in to comment.