Skip to content

Commit 77a22f2

Browse files
pc-for-sciencesgued
authored andcommitted
Made Node trait functions unsafe and guaranteed correct initialization in ArcPool and BoxPool
1 parent db731c3 commit 77a22f2

File tree

5 files changed

+64
-13
lines changed

5 files changed

+64
-13
lines changed

src/pool/arc.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
use core::{
7373
fmt,
7474
hash::{Hash, Hasher},
75-
mem::{ManuallyDrop, MaybeUninit},
75+
mem::MaybeUninit,
7676
ops, ptr,
7777
};
7878

@@ -188,6 +188,11 @@ impl<T> ArcPoolImpl<T> {
188188
fn manage(&self, block: &'static mut ArcBlock<T>) {
189189
let node: &'static mut _ = &mut block.node;
190190

191+
// SAFETY: The node within an `ArcBlock` is always properly initialized for linking because
192+
// the only way for client code to construct an `ArcBlock` is through
193+
// `ArcBlock::new`. The `NonNullPtr` comes from a reference, so it is
194+
// guaranteed to be dereferencable. It is also unique because the `ArcBlock` itself
195+
// is passed as a `&mut`
191196
unsafe { self.stack.push(NonNullPtr::from_static_mut_ref(node)) }
192197
}
193198
}
@@ -383,9 +388,7 @@ impl<T> ArcBlock<T> {
383388
/// Creates a new memory block
384389
pub const fn new() -> Self {
385390
Self {
386-
node: UnionNode {
387-
data: ManuallyDrop::new(MaybeUninit::uninit()),
388-
},
391+
node: UnionNode::unlinked(),
389392
}
390393
}
391394
}

src/pool/boxed.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,11 @@ impl<T> BoxPoolImpl<T> {
376376
fn manage(&self, block: &'static mut BoxBlock<T>) {
377377
let node: &'static mut _ = &mut block.node;
378378

379+
// SAFETY: The node within a `BoxBlock` is always properly initialized for linking because
380+
// the only way for client code to construct a `BoxBlock` is through
381+
// `BoxBlock::new`. The `NonNullPtr` comes from a reference, so it is
382+
// guaranteed to be dereferencable. It is also unique because the `BoxBlock` itself
383+
// is passed as a `&mut`
379384
unsafe { self.stack.push(NonNullPtr::from_static_mut_ref(node)) }
380385
}
381386
}
@@ -391,9 +396,7 @@ impl<T> BoxBlock<T> {
391396
/// Creates a new memory block
392397
pub const fn new() -> Self {
393398
Self {
394-
node: UnionNode {
395-
data: ManuallyDrop::new(MaybeUninit::uninit()),
396-
},
399+
node: UnionNode::unlinked(),
397400
}
398401
}
399402
}

src/pool/treiber.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ where
2727
/// - `node` must be a valid pointer
2828
/// - aliasing rules must be enforced by the caller. e.g, the same `node` may not be pushed more
2929
/// than once
30+
/// - It must be valid to call `next()` on the `node`, meaning it must be properly initialized
31+
/// for insertion into a stack/linked list
3032
pub unsafe fn push(&self, node: NonNullPtr<N>) {
3133
impl_::push(self, node);
3234
}
@@ -39,10 +41,22 @@ where
3941
pub trait Node: Sized {
4042
type Data;
4143

42-
fn next(&self) -> &AtomicPtr<Self>;
44+
/// Returns a reference to the atomic pointer that stores the link to the next `Node`
45+
///
46+
/// # Safety
47+
///
48+
/// It must be valid to obtain a reference to the next link pointer, e.g. in the case of
49+
/// `UnionNode`, the `next` field must be properly initialized when calling this function
50+
unsafe fn next(&self) -> &AtomicPtr<Self>;
4351

52+
/// Returns a mutable reference to the atomic pointer that stores the link to the next `Node`
53+
///
54+
/// # Safety
55+
///
56+
/// It must be valid to obtain a reference to the next link pointer, e.g. in the case of
57+
/// `UnionNode`, the `next` field must be properly initialized when calling this function
4458
#[allow(dead_code)] // used conditionally
45-
fn next_mut(&mut self) -> &mut AtomicPtr<Self>;
59+
unsafe fn next_mut(&mut self) -> &mut AtomicPtr<Self>;
4660
}
4761

4862
#[repr(C)]
@@ -51,14 +65,28 @@ pub union UnionNode<T> {
5165
pub data: ManuallyDrop<T>,
5266
}
5367

68+
impl<T> UnionNode<T> {
69+
/// Returns a new `UnionNode` that does not contain data and is not linked to any other nodes.
70+
/// The return value of this function is guaranteed to have the `next` field properly
71+
/// initialized. Use this function if you want to insert a new `UnionNode` into a linked
72+
/// list
73+
pub const fn unlinked() -> Self {
74+
Self {
75+
next: ManuallyDrop::new(AtomicPtr::null()),
76+
}
77+
}
78+
}
79+
5480
impl<T> Node for UnionNode<T> {
5581
type Data = T;
5682

57-
fn next(&self) -> &AtomicPtr<Self> {
83+
unsafe fn next(&self) -> &AtomicPtr<Self> {
84+
// SAFETY: Caller ensures that `self.next` is properly initialized
5885
unsafe { &self.next }
5986
}
6087

61-
fn next_mut(&mut self) -> &mut AtomicPtr<Self> {
88+
unsafe fn next_mut(&mut self) -> &mut AtomicPtr<Self> {
89+
// SAFETY: Caller ensures that `self.next` is properly initialized
6290
unsafe { &mut self.next }
6391
}
6492
}
@@ -71,11 +99,11 @@ pub struct StructNode<T> {
7199
impl<T> Node for StructNode<T> {
72100
type Data = T;
73101

74-
fn next(&self) -> &AtomicPtr<Self> {
102+
unsafe fn next(&self) -> &AtomicPtr<Self> {
75103
&self.next
76104
}
77105

78-
fn next_mut(&mut self) -> &mut AtomicPtr<Self> {
106+
unsafe fn next_mut(&mut self) -> &mut AtomicPtr<Self> {
79107
&mut self.next
80108
}
81109
}

src/pool/treiber/cas.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,13 @@ const fn initial_tag() -> Tag {
188188
Tag::MIN
189189
}
190190

191+
/// Pushes the given node on top of the stack
192+
///
193+
/// # Safety
194+
///
195+
/// - `new_top` must point to a node that is properly initialized for linking, i.e.
196+
/// `new_top.as_ref().next()` must be valid to call (see [`Node::next`])
197+
/// - `new_top` must be convertible to a reference (see [`NonNull::as_ref`])
191198
pub unsafe fn push<N>(stack: &Stack<N>, new_top: NonNullPtr<N>)
192199
where
193200
N: Node,
@@ -198,6 +205,7 @@ where
198205
new_top
199206
.non_null()
200207
.as_ref()
208+
// SAFETY: Caller ensures that it is safe to call `next`
201209
.next()
202210
.store(top, Ordering::Relaxed);
203211

src/pool/treiber/llsc.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ where
6767

6868
impl<N> Copy for NonNullPtr<N> where N: Node {}
6969

70+
/// Pushes the given node on top of the stack
71+
///
72+
/// # Safety
73+
///
74+
/// - `node` must point to a node that is properly initialized for linking, i.e.
75+
/// `node.as_mut().next_mut()` must be valid to call (see [`Node::next_mut`])
76+
/// - `node` must be convertible to a reference (see [`NonNull::as_mut`])
7077
pub unsafe fn push<N>(stack: &Stack<N>, mut node: NonNullPtr<N>)
7178
where
7279
N: Node,
@@ -78,9 +85,11 @@ where
7885

7986
node.inner
8087
.as_mut()
88+
// SAFETY: Caller guarantees that it is valid to call `next_mut`
8189
.next_mut()
8290
.inner
8391
.get()
92+
// SAFETY: The pointer comes from `AtomicPtr::inner`, which is valid for writes
8493
.write(NonNull::new(top as *mut _));
8594

8695
if arch::store_conditional(node.inner.as_ptr() as usize, top_addr).is_ok() {

0 commit comments

Comments
 (0)