Skip to content

Commit b80ead5

Browse files
authored
struct StableRef: Refactor out from CArc::{,base_}stable_ref and unsafe impl Send + Sync on it (#1282)
* Part of #1177. This makes `CArc<T: Send + Sync>: Send + Sync`. Accidentally "merged" #1278 (it says merged but I didn't merge it) when I flipped the order of the branches and PRs.
2 parents 81e599f + 4db9d24 commit b80ead5

File tree

1 file changed

+35
-8
lines changed

1 file changed

+35
-8
lines changed

src/c_arc.rs

+35-8
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,37 @@ pub struct CArc<T: ?Sized> {
4242

4343
/// The same as [`Self::stable_ref`] but it never changes.
4444
#[cfg(debug_assertions)]
45-
base_stable_ref: NonNull<T>,
45+
base_stable_ref: StableRef<T>,
4646

47-
stable_ref: NonNull<T>,
47+
stable_ref: StableRef<T>,
4848
}
4949

50+
/// A stable reference, stored as a raw ptr.
51+
///
52+
/// # Safety
53+
///
54+
/// The raw ptr of a [`StableRef`] must have a stable address.
55+
/// Even if `T`'s owning type, e.x. a [`Box`]`<T>`, is moved,
56+
/// ptrs to `T` must remain valid and thus "stable".
57+
///
58+
/// Thus, it can be stored relative to its owner.
59+
#[derive(Debug)]
60+
struct StableRef<T: ?Sized>(NonNull<T>);
61+
62+
impl<T: ?Sized> Clone for StableRef<T> {
63+
fn clone(&self) -> Self {
64+
*self
65+
}
66+
}
67+
68+
impl<T: ?Sized> Copy for StableRef<T> {}
69+
70+
/// SAFETY: [`StableRef`]`<T>`, if it follows its safety guarantees, is essentially a `&T`/`&mut T`, which is [`Send`] if `T: `[`Send`]`.
71+
unsafe impl<T: Send + ?Sized> Send for StableRef<T> {}
72+
73+
/// SAFETY: [`StableRef`]`<T>`, if it follows its safety guarantees, is essentially a `&T`/`&mut T`, which is [`Sync`] if `T: `[`Sync`].
74+
unsafe impl<T: Send + ?Sized> Sync for StableRef<T> {}
75+
5076
impl<T: ?Sized> AsRef<T> for CArc<T> {
5177
fn as_ref(&self) -> &T {
5278
#[cfg(debug_assertions)]
@@ -57,12 +83,13 @@ impl<T: ?Sized> AsRef<T> for CArc<T> {
5783
// Some extra checks to check if our ptrs are definitely invalid.
5884

5985
let real_ref = (*self.owner).as_ref().get_ref();
60-
assert_eq!(real_ref.to::<NonNull<T>>(), self.base_stable_ref);
86+
assert_eq!(real_ref.to::<NonNull<T>>(), self.base_stable_ref.0);
6187

6288
// Cast through `*const ()` and use [`pointer::byte_offset_from`]
6389
// to remove any fat ptr metadata.
6490
let offset = unsafe {
6591
self.stable_ref
92+
.0
6693
.as_ptr()
6794
.cast::<()>()
6895
.byte_offset_from((real_ref as *const T).cast::<()>())
@@ -72,7 +99,7 @@ impl<T: ?Sized> AsRef<T> for CArc<T> {
7299
let out_of_bounds = offset > len;
73100
if out_of_bounds {
74101
dbg!(real_ref as *const T);
75-
dbg!(self.stable_ref.as_ptr());
102+
dbg!(self.stable_ref.0.as_ptr());
76103
dbg!(offset);
77104
dbg!(len);
78105
panic!("CArc::stable_ref is out of bounds");
@@ -83,9 +110,9 @@ impl<T: ?Sized> AsRef<T> for CArc<T> {
83110
// derived from [`Self::owner`]'s through [`CBox::as_ref`]
84111
// and is thus safe to dereference.
85112
// The [`CBox`] is [`Pin`]ned and
86-
// [`Self::stable_Ref`] is always updated on writes to [`Self::owner`],
113+
// [`Self::stable_ref`] is always updated on writes to [`Self::owner`],
87114
// so they are always in sync.
88-
unsafe { self.stable_ref.as_ref() }
115+
unsafe { self.stable_ref.0.as_ref() }
89116
}
90117
}
91118

@@ -116,7 +143,7 @@ impl<T: ?Sized> Clone for CArc<T> {
116143

117144
impl<T: ?Sized> From<Arc<Pin<CBox<T>>>> for CArc<T> {
118145
fn from(owner: Arc<Pin<CBox<T>>>) -> Self {
119-
let stable_ref = (*owner).as_ref().get_ref().into();
146+
let stable_ref = StableRef((*owner).as_ref().get_ref().into());
120147
Self {
121148
owner,
122149
#[cfg(debug_assertions)]
@@ -229,7 +256,7 @@ impl<T> CArc<[T]> {
229256
where
230257
I: SliceIndex<[T], Output = [T]>,
231258
{
232-
self.stable_ref = self.as_ref()[range].into();
259+
self.stable_ref = StableRef(self.as_ref()[range].into());
233260
}
234261

235262
pub fn split_at(this: Self, mid: usize) -> (Self, Self) {

0 commit comments

Comments
 (0)