Skip to content

Commit b7a9e83

Browse files
Made Node trait functions unsafe and guaranteed correct initialization in ArcPool and BoxPool
1 parent ceb3fc0 commit b7a9e83

File tree

5 files changed

+62
-14
lines changed

5 files changed

+62
-14
lines changed

src/pool/arc.rs

Lines changed: 6 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,10 @@ 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 the only way for
192+
// client code to construct an `ArcBlock` is through `ArcBlock::new`. The `NonNullPtr` comes from a
193+
// reference, so it is guaranteed to be dereferencable. It is also unique because the `ArcBlock` itself
194+
// is passed as a `&mut`
191195
unsafe { self.stack.push(NonNullPtr::from_static_mut_ref(node)) }
192196
}
193197
}
@@ -383,9 +387,7 @@ impl<T> ArcBlock<T> {
383387
/// Creates a new memory block
384388
pub const fn new() -> Self {
385389
Self {
386-
node: UnionNode {
387-
data: ManuallyDrop::new(MaybeUninit::uninit()),
388-
},
390+
node: UnionNode::unlinked(),
389391
}
390392
}
391393
}

src/pool/boxed.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
use core::{
8484
fmt,
8585
hash::{Hash, Hasher},
86-
mem::{ManuallyDrop, MaybeUninit},
86+
mem::MaybeUninit,
8787
ops, ptr,
8888
};
8989

@@ -339,6 +339,10 @@ impl<T> BoxPoolImpl<T> {
339339
fn manage(&self, block: &'static mut BoxBlock<T>) {
340340
let node: &'static mut _ = &mut block.node;
341341

342+
// SAFETY: The node within a `BoxBlock` is always properly initialized for linking because the only way for
343+
// client code to construct a `BoxBlock` is through `BoxBlock::new`. The `NonNullPtr` comes from a
344+
// reference, so it is guaranteed to be dereferencable. It is also unique because the `BoxBlock` itself
345+
// is passed as a `&mut`
342346
unsafe { self.stack.push(NonNullPtr::from_static_mut_ref(node)) }
343347
}
344348
}
@@ -354,9 +358,7 @@ impl<T> BoxBlock<T> {
354358
/// Creates a new memory block
355359
pub const fn new() -> Self {
356360
Self {
357-
node: UnionNode {
358-
data: ManuallyDrop::new(MaybeUninit::uninit()),
359-
},
361+
node: UnionNode::unlinked(),
360362
}
361363
}
362364
}

src/pool/treiber.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ where
2626
/// # Safety
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 than once
29+
/// - It must be valid to call `next()` on the `node`, meaning it must be properly initialized for insertion
30+
/// into a stack/linked list
2931
pub unsafe fn push(&self, node: NonNullPtr<N>) {
3032
impl_::push(self, node);
3133
}
@@ -38,10 +40,22 @@ where
3840
pub trait Node: Sized {
3941
type Data;
4042

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

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

4761
#[repr(C)]
@@ -50,14 +64,27 @@ pub union UnionNode<T> {
5064
pub data: ManuallyDrop<T>,
5165
}
5266

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

56-
fn next(&self) -> &AtomicPtr<Self> {
81+
unsafe fn next(&self) -> &AtomicPtr<Self> {
82+
// SAFETY: Caller ensures that `self.next` is properly initialized
5783
unsafe { &self.next }
5884
}
5985

60-
fn next_mut(&mut self) -> &mut AtomicPtr<Self> {
86+
unsafe fn next_mut(&mut self) -> &mut AtomicPtr<Self> {
87+
// SAFETY: Caller ensures that `self.next` is properly initialized
6188
unsafe { &mut self.next }
6289
}
6390
}
@@ -70,11 +97,11 @@ pub struct StructNode<T> {
7097
impl<T> Node for StructNode<T> {
7198
type Data = T;
7299

73-
fn next(&self) -> &AtomicPtr<Self> {
100+
unsafe fn next(&self) -> &AtomicPtr<Self> {
74101
&self.next
75102
}
76103

77-
fn next_mut(&mut self) -> &mut AtomicPtr<Self> {
104+
unsafe fn next_mut(&mut self) -> &mut AtomicPtr<Self> {
78105
&mut self.next
79106
}
80107
}

src/pool/treiber/cas.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,13 @@ const fn initial_tag() -> Tag {
182182
Tag::MIN
183183
}
184184

185+
/// Pushes the given node on top of the stack
186+
///
187+
/// # Safety
188+
///
189+
/// - `new_top` must point to a node that is properly initialized for linking, i.e. `new_top.as_ref().next()`
190+
/// must be valid to call (see [`Node::next`])
191+
/// - `new_top` must be convertible to a reference (see [`NonNull::as_ref`])
185192
pub unsafe fn push<N>(stack: &Stack<N>, new_top: NonNullPtr<N>)
186193
where
187194
N: Node,
@@ -192,6 +199,7 @@ where
192199
new_top
193200
.non_null()
194201
.as_ref()
202+
// SAFETY: Caller ensures that it is safe to call `next`
195203
.next()
196204
.store(top, Ordering::Relaxed);
197205

src/pool/treiber/llsc.rs

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

6060
impl<N> Copy for NonNullPtr<N> where N: Node {}
6161

62+
/// Pushes the given node on top of the stack
63+
///
64+
/// # Safety
65+
///
66+
/// - `node` must point to a node that is properly initialized for linking, i.e. `node.as_mut().next_mut()`
67+
/// must be valid to call (see [`Node::next_mut`])
68+
/// - `node` must be convertible to a reference (see [`NonNull::as_mut`])
6269
pub unsafe fn push<N>(stack: &Stack<N>, mut node: NonNullPtr<N>)
6370
where
6471
N: Node,
@@ -70,9 +77,11 @@ where
7077

7178
node.inner
7279
.as_mut()
80+
// SAFETY: Caller guarantees that it is valid to call `next_mut`
7381
.next_mut()
7482
.inner
7583
.get()
84+
// SAFETY: The pointer comes from `AtomicPtr::inner`, which is valid for writes
7685
.write(NonNull::new(top as *mut _));
7786

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

0 commit comments

Comments
 (0)