Skip to content

Commit 5eadfca

Browse files
author
Balint134
committed
Add support for into_raw and from_raw for Box
1 parent db26571 commit 5eadfca

File tree

3 files changed

+103
-1
lines changed

3 files changed

+103
-1
lines changed

src/pool/boxed.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ use core::{
8585
hash::{Hash, Hasher},
8686
mem::{ManuallyDrop, MaybeUninit},
8787
ops, ptr,
88+
ptr::{addr_of, addr_of_mut},
8889
};
89-
9090
use stable_deref_trait::StableDeref;
9191

9292
use super::treiber::{NonNullPtr, Stack, UnionNode};
@@ -180,6 +180,41 @@ where
180180
node_ptr: NonNullPtr<UnionNode<MaybeUninit<P::Data>>>,
181181
}
182182

183+
impl<P> Box<P>
184+
where
185+
P: BoxPool,
186+
{
187+
/// Consumes the `Box`, returning the wrapped pointer
188+
pub fn into_raw(b: Self) -> *mut P::Data {
189+
let mut b = ManuallyDrop::new(b);
190+
// SAFETY: `b` is not dropped, so the pointer remains valid however caller must ensure that eventually it is returned to the pool
191+
addr_of_mut!(**b)
192+
}
193+
194+
/// Constructs a `Box<P>` from a raw pointer
195+
///
196+
/// # Safety
197+
///
198+
/// The `ptr` must have been allocated by the same `BoxPool` `P` that this `Box<P>` uses.
199+
pub unsafe fn from_raw(ptr: *mut P::Data) -> Self {
200+
// SAFETY: caller must guarantee that `ptr` is valid and was allocated by `P`
201+
debug_assert!(!ptr.is_null(), "Pointer must be non-null");
202+
203+
// Calculate the offset of the `data` field within `UnionNode` (with the current layout tha data is at the beginning so offset is zero, but this may change in the future)
204+
let uninit_union_node = MaybeUninit::<UnionNode<MaybeUninit<P::Data>>>::uninit();
205+
let data_ptr = unsafe { addr_of!((*uninit_union_node.as_ptr()).data) };
206+
let data_offset = (data_ptr as usize) - (uninit_union_node.as_ptr() as usize);
207+
let union_node_ptr = ptr
208+
.cast::<u8>()
209+
.sub(data_offset)
210+
.cast::<UnionNode<MaybeUninit<P::Data>>>();
211+
212+
Self {
213+
node_ptr: NonNullPtr::new_unchecked_from_ptr(union_node_ptr),
214+
}
215+
}
216+
}
217+
183218
impl<A> Clone for Box<A>
184219
where
185220
A: BoxPool,
@@ -568,4 +603,57 @@ mod tests {
568603
assert!(once.is_ok());
569604
assert!(twice.is_ok());
570605
}
606+
607+
#[test]
608+
fn test_into_raw_from_raw() {
609+
pub struct MyStruct {
610+
value: [u8; 64],
611+
}
612+
613+
static DROP_WAS_CALLED: AtomicUsize = AtomicUsize::new(0);
614+
impl Drop for MyStruct {
615+
fn drop(&mut self) {
616+
DROP_WAS_CALLED.fetch_add(1, Ordering::AcqRel);
617+
}
618+
}
619+
620+
box_pool!(MyBoxPool: MyStruct);
621+
622+
MyBoxPool.manage(unsafe {
623+
static mut BLOCK: BoxBlock<MyStruct> = BoxBlock::new();
624+
addr_of_mut!(BLOCK).as_mut().unwrap()
625+
});
626+
627+
MyBoxPool.manage(unsafe {
628+
static mut BLOCK: BoxBlock<MyStruct> = BoxBlock::new();
629+
addr_of_mut!(BLOCK).as_mut().unwrap()
630+
});
631+
632+
let raw = {
633+
let boxed = MyBoxPool
634+
.alloc(MyStruct { value: [0xA5; 64] })
635+
.ok()
636+
.unwrap();
637+
Box::into_raw(boxed)
638+
};
639+
assert_eq!(0, DROP_WAS_CALLED.load(Ordering::Acquire));
640+
let addr_1 = raw as usize;
641+
642+
{
643+
let boxed_again: Box<MyBoxPool> = unsafe { Box::from_raw(raw) };
644+
let addr_2 = boxed_again.node_ptr.as_ptr() as usize;
645+
assert_eq!([0xA5; 64], boxed_again.value);
646+
assert_eq!(addr_1, addr_2);
647+
}
648+
assert_eq!(1, DROP_WAS_CALLED.load(Ordering::Acquire));
649+
650+
// Allocate again to ensure memory was returned to the pool
651+
let boxed_2 = MyBoxPool
652+
.alloc(MyStruct { value: [0xEF; 64] })
653+
.ok()
654+
.unwrap();
655+
let addr_2 = boxed_2.node_ptr.as_ptr() as usize;
656+
assert_eq!([0xEF; 64], boxed_2.value);
657+
assert_eq!(addr_1, addr_2);
658+
}
571659
}

src/pool/treiber/cas.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ where
125125
unsafe { Self::new_unchecked(initial_tag(), NonNull::from(reference)) }
126126
}
127127

128+
#[inline]
129+
pub unsafe fn new_unchecked_from_ptr(ptr: *mut N) -> Self {
130+
debug_assert!(!ptr.is_null(), "Pointer must be non-null");
131+
Self::new_unchecked(initial_tag(), NonNull::new_unchecked(ptr))
132+
}
133+
128134
/// # Safety
129135
///
130136
/// - `ptr` must be a valid pointer.

src/pool/treiber/llsc.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ where
4646
inner: NonNull::from(ref_),
4747
}
4848
}
49+
50+
#[inline]
51+
pub unsafe fn new_unchecked_from_ptr(ptr: *mut N) -> Self {
52+
debug_assert!(!ptr.is_null(), "Pointer must be non-null");
53+
Self {
54+
inner: NonNull::new_unchecked(ptr),
55+
}
56+
}
4957
}
5058

5159
impl<N> Clone for NonNullPtr<N>

0 commit comments

Comments
 (0)