Skip to content

Commit 694bb8f

Browse files
pc-for-sciencesgued
authored andcommitted
Made Node trait functions unsafe and guaranteed correct initialization in ArcPool and BoxPool
1 parent dfe1441 commit 694bb8f

File tree

5 files changed

+61
-13
lines changed

5 files changed

+61
-13
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: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,10 @@ 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 the only way for
380+
// client code to construct a `BoxBlock` is through `BoxBlock::new`. The `NonNullPtr` comes from a
381+
// reference, so it is guaranteed to be dereferencable. It is also unique because the `BoxBlock` itself
382+
// is passed as a `&mut`
379383
unsafe { self.stack.push(NonNullPtr::from_static_mut_ref(node)) }
380384
}
381385
}
@@ -391,9 +395,7 @@ impl<T> BoxBlock<T> {
391395
/// Creates a new memory block
392396
pub const fn new() -> Self {
393397
Self {
394-
node: UnionNode {
395-
data: ManuallyDrop::new(MaybeUninit::uninit()),
396-
},
398+
node: UnionNode::unlinked(),
397399
}
398400
}
399401
}

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
@@ -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. `new_top.as_ref().next()`
196+
/// 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. `node.as_mut().next_mut()`
75+
/// 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)