Skip to content

Commit 08061bd

Browse files
authored
Merge pull request #432 from wedsonaf/ref-raw
rust: add `Ref::into_raw` and `Ref::from_raw`.
2 parents bca8dbc + 75a8d26 commit 08061bd

File tree

2 files changed

+60
-9
lines changed

2 files changed

+60
-9
lines changed

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
const_raw_ptr_deref,
2222
const_unreachable_unchecked,
2323
doc_cfg,
24+
ptr_metadata,
2425
receiver_trait,
2526
coerce_unsized,
2627
dispatch_from_dyn,

rust/kernel/sync/arc.rs

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
use crate::{bindings, Result};
1919
use alloc::boxed::Box;
2020
use core::{
21+
alloc::Layout,
2122
cell::UnsafeCell,
2223
convert::AsRef,
2324
marker::{PhantomData, Unsize},
2425
mem::ManuallyDrop,
2526
ops::Deref,
2627
pin::Pin,
27-
ptr::NonNull,
28+
ptr::{self, NonNull},
2829
};
2930

3031
extern "C" {
@@ -47,6 +48,7 @@ pub struct Ref<T: ?Sized> {
4748
_p: PhantomData<RefInner<T>>,
4849
}
4950

51+
#[repr(C)]
5052
struct RefInner<T: ?Sized> {
5153
refcount: UnsafeCell<bindings::refcount_t>,
5254
data: T,
@@ -97,10 +99,9 @@ impl<T> Ref<T> {
9799
// pinned, and in `drop`. Both are compatible with the pin requirements.
98100
init(pinned);
99101

100-
Ok(Ref {
101-
ptr: NonNull::from(Box::leak(inner)),
102-
_p: PhantomData,
103-
})
102+
// SAFETY: We just created `inner` with a reference count of 1 and we're leaking it. So the
103+
// new `Ref` object owns the reference.
104+
Ok(unsafe { Self::from_inner(NonNull::from(Box::leak(inner))) })
104105
}
105106

106107
/// Deconstructs a [`Ref`] object into a `usize`.
@@ -134,24 +135,73 @@ impl<T> Ref<T> {
134135
/// `encoded` must have been returned by a previous call to [`Ref::into_usize`]. Additionally,
135136
/// it can only be called once for each previous call to [``Ref::into_usize`].
136137
pub unsafe fn from_usize(encoded: usize) -> Self {
138+
// SAFETY: By the safety invariants we know that `encoded` came from `Ref::into_usize`, so
139+
// the reference count held then will be owned by the new `Ref` object.
140+
unsafe { Self::from_inner(NonNull::new(encoded as _).unwrap()) }
141+
}
142+
}
143+
144+
impl<T: ?Sized> Ref<T> {
145+
/// Constructs a new [`Ref`] from an existing [`RefInner`].
146+
///
147+
/// # Safety
148+
///
149+
/// The caller must ensure that `inner` points to a valid location and has a non-zero reference
150+
/// count, one of which will be owned by the new [`Ref`] instance.
151+
unsafe fn from_inner(inner: NonNull<RefInner<T>>) -> Self {
152+
// INVARIANT: By the safety requirements, the invariants hold.
137153
Ref {
138-
ptr: NonNull::new(encoded as _).unwrap(),
154+
ptr: inner,
139155
_p: PhantomData,
140156
}
141157
}
142-
}
143158

144-
impl<T: ?Sized> Ref<T> {
145159
/// Determines if two reference-counted pointers point to the same underlying instance of `T`.
146160
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
147-
core::ptr::eq(a.ptr.as_ptr(), b.ptr.as_ptr())
161+
ptr::eq(a.ptr.as_ptr(), b.ptr.as_ptr())
148162
}
149163

150164
/// Returns a pinned version of a given `Ref` instance.
151165
pub fn pinned(obj: Self) -> Pin<Self> {
152166
// SAFETY: The type invariants guarantee that the value is pinned.
153167
unsafe { Pin::new_unchecked(obj) }
154168
}
169+
170+
/// Deconstructs a [`Ref`] object into a raw pointer.
171+
///
172+
/// It can be reconstructed once via [`Ref::from_raw`].
173+
pub fn into_raw(obj: Self) -> *const T {
174+
let ret = &*obj as *const T;
175+
core::mem::forget(obj);
176+
ret
177+
}
178+
179+
/// Recreates a [`Ref`] instance previously deconstructed via [`Ref::into_raw`].
180+
///
181+
/// This code relies on the `repr(C)` layout of structs as described in
182+
/// <https://doc.rust-lang.org/reference/type-layout.html#reprc-structs>.
183+
///
184+
/// # Safety
185+
///
186+
/// `ptr` must have been returned by a previous call to [`Ref::into_raw`]. Additionally, it
187+
/// can only be called once for each previous call to [``Ref::into_raw`].
188+
pub unsafe fn from_raw(ptr: *const T) -> Self {
189+
// SAFETY: The safety requirement ensures that the pointer is valid.
190+
let align = core::mem::align_of_val(unsafe { &*ptr });
191+
let offset = Layout::new::<RefInner<()>>()
192+
.align_to(align)
193+
.unwrap()
194+
.pad_to_align()
195+
.size();
196+
// SAFETY: The pointer is in bounds because by the safety requirements `ptr` came from
197+
// `Ref::into_raw`, so it is a pointer `offset` bytes from the beginning of the allocation.
198+
let data = unsafe { (ptr as *const u8).sub(offset) };
199+
let metadata = ptr::metadata(ptr as *const RefInner<T>);
200+
let ptr = ptr::from_raw_parts_mut(data as _, metadata);
201+
// SAFETY: By the safety requirements we know that `ptr` came from `Ref::into_raw`, so the
202+
// reference count held then will be owned by the new `Ref` object.
203+
unsafe { Self::from_inner(NonNull::new(ptr).unwrap()) }
204+
}
155205
}
156206

157207
impl<T: ?Sized> Deref for Ref<T> {

0 commit comments

Comments
 (0)