Skip to content

Commit b9d8c65

Browse files
committed
Add Ref to the sync module.
1 parent d72964e commit b9d8c65

File tree

3 files changed

+179
-0
lines changed

3 files changed

+179
-0
lines changed

rust/kernel/file_operations.rs

+11
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use alloc::sync::Arc;
1313
use crate::bindings;
1414
use crate::c_types;
1515
use crate::error::{Error, KernelResult};
16+
use crate::sync::{Ref, RefCounted};
1617
use crate::user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter};
1718

1819
/// Wraps the kernel's `struct file`.
@@ -506,6 +507,16 @@ impl<T> PointerWrapper<T> for Box<T> {
506507
}
507508
}
508509

510+
impl<T: RefCounted> PointerWrapper<T> for Ref<T> {
511+
fn into_pointer(self) -> *const T {
512+
Ref::into_raw(self)
513+
}
514+
515+
unsafe fn from_pointer(ptr: *const T) -> Self {
516+
Ref::from_raw(ptr as _)
517+
}
518+
}
519+
509520
impl<T> PointerWrapper<T> for Arc<T> {
510521
fn into_pointer(self) -> *const T {
511522
Arc::into_raw(self)

rust/kernel/sync/arc.rs

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! A reference-counted pointer.
4+
//!
5+
//! This module implements a way for users to create reference-counted objects and pointers to
6+
//! them. Such a pointer automatically increments and decrements the count, and drops the
7+
//! underlying object when it reaches zero.
8+
//!
9+
//! It is different from the standard library's [`Arc`] in two ways: it does not support weak
10+
//! references, which allows it to be smaller -- a single pointer-sized integer; it allows users to
11+
//! safely increment the reference count from a single reference to the underlying object.
12+
//!
13+
//! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
14+
15+
use crate::KernelResult;
16+
use alloc::boxed::Box;
17+
use core::{
18+
mem::ManuallyDrop,
19+
ops::Deref,
20+
ptr::NonNull,
21+
sync::atomic::{AtomicUsize, Ordering},
22+
};
23+
24+
/// A reference-counted pointer to an instance of `T`.
25+
///
26+
/// The reference count is incremented when new instances of [`Ref`] are created, and decremented
27+
/// when they are dropped. When the count reaches zero, the underlying `T` is also dropped.
28+
///
29+
/// # Invariants
30+
///
31+
/// The value stored in [`RefCounted::get_count`] corresponds to the number of instances of [`Ref`]
32+
/// that point to that instance of `T`.
33+
pub struct Ref<T: RefCounted + ?Sized> {
34+
ptr: NonNull<T>,
35+
}
36+
37+
impl<T: RefCounted> Ref<T> {
38+
/// Constructs a new reference counted instance of `T`.
39+
pub fn try_new(contents: T) -> KernelResult<Self> {
40+
let boxed = Box::try_new(contents)?;
41+
boxed.get_count().count.store(1, Ordering::Release);
42+
let ptr = NonNull::from(boxed.deref());
43+
Box::into_raw(boxed);
44+
Ok(Ref { ptr })
45+
}
46+
}
47+
48+
impl<T: RefCounted + ?Sized> Ref<T> {
49+
/// Creates a new reference-counted pointer to the given instance of `T`.
50+
///
51+
/// It works by incrementing the current reference count as part of constructing the new
52+
/// pointer.
53+
pub fn new_from(obj: &T) -> Self {
54+
let ref_count = obj.get_count();
55+
let cur = ref_count.count.fetch_add(1, Ordering::Relaxed);
56+
if cur == usize::MAX {
57+
panic!("Reference counted overflowed");
58+
}
59+
Self {
60+
ptr: NonNull::from(obj),
61+
}
62+
}
63+
64+
/// Returns a mutable reference to `T` iff the reference count is one. Otherwise returns
65+
/// [`None`].
66+
pub fn get_mut(&mut self) -> Option<&mut T> {
67+
if self.get_count().count.load(Ordering::Acquire) != 1 {
68+
return None;
69+
}
70+
// SAFETY: Since there is only one reference, we know it isn't possible for another thread
71+
// to concurrently call this.
72+
Some(unsafe { self.ptr.as_mut() })
73+
}
74+
75+
/// Determines if two reference-counted pointers point to the same underlying instance of `T`.
76+
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
77+
core::ptr::eq(a.ptr.as_ptr(), b.ptr.as_ptr())
78+
}
79+
80+
/// Deconstructs a [`Ref`] object into a raw pointer.
81+
///
82+
/// It can be reconstructed once via [`Ref::from_raw`].
83+
pub fn into_raw(obj: Self) -> *const T {
84+
let no_drop = ManuallyDrop::new(obj);
85+
no_drop.ptr.as_ptr()
86+
}
87+
88+
/// Recreates a [`Ref`] instance previously deconstructed via [`Ref::into_raw`].
89+
///
90+
/// # Safety
91+
///
92+
/// `ptr` must have been returned by a previous call to [`Ref::into_raw`]. Additionally, it
93+
/// can only be called once for each previous call to [``Ref::into_raw`].
94+
pub unsafe fn from_raw(ptr: *const T) -> Self {
95+
Ref {
96+
ptr: NonNull::new(ptr as _).unwrap(),
97+
}
98+
}
99+
}
100+
101+
impl<T: RefCounted + ?Sized> Deref for Ref<T> {
102+
type Target = T;
103+
104+
fn deref(&self) -> &Self::Target {
105+
// SAFETY: By the type invariant, there is necessarily a reference to the object, so it is
106+
// safe to dereference it.
107+
unsafe { self.ptr.as_ref() }
108+
}
109+
}
110+
111+
impl<T: RefCounted + ?Sized> Clone for Ref<T> {
112+
fn clone(&self) -> Self {
113+
Self::new_from(self)
114+
}
115+
}
116+
117+
impl<T: RefCounted + ?Sized> Drop for Ref<T> {
118+
fn drop(&mut self) {
119+
{
120+
// SAFETY: By the type invariant, there is necessarily a reference to the object.
121+
let obj = unsafe { self.ptr.as_ref() };
122+
if obj.get_count().count.fetch_sub(1, Ordering::Release) != 1 {
123+
return;
124+
}
125+
}
126+
127+
// The count reached zero, we must free the memory.
128+
//
129+
// SAFETY: The pointer was initialised from the result of `Box::into_raw`.
130+
unsafe { Box::from_raw(self.ptr.as_ptr()) };
131+
}
132+
}
133+
134+
/// Trait for reference counted objects.
135+
///
136+
/// # Safety
137+
///
138+
/// Implementers of [`RefCounted`] must ensure that all of their constructors call
139+
/// [`Ref::try_new`].
140+
pub unsafe trait RefCounted: Sized {
141+
/// Returns a pointer to the object field holds the reference count.
142+
fn get_count(&self) -> &RefCount;
143+
}
144+
145+
/// Holds the reference count of an object.
146+
///
147+
/// It is meant to be embedded in objects to be reference-counted, with [`RefCounted::get_count`]
148+
/// returning a reference to it.
149+
pub struct RefCount {
150+
count: AtomicUsize,
151+
}
152+
153+
impl RefCount {
154+
/// Constructs a new instance of [`RefCount`].
155+
pub fn new() -> Self {
156+
Self {
157+
count: AtomicUsize::new(1),
158+
}
159+
}
160+
}
161+
162+
impl Default for RefCount {
163+
fn default() -> Self {
164+
Self::new()
165+
}
166+
}

rust/kernel/sync/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020
use crate::{bindings, CStr};
2121
use core::pin::Pin;
2222

23+
mod arc;
2324
mod condvar;
2425
mod guard;
2526
mod locked_by;
2627
mod mutex;
2728
mod spinlock;
2829

30+
pub use arc::{Ref, RefCount, RefCounted};
2931
pub use condvar::CondVar;
3032
pub use guard::{Guard, Lock};
3133
pub use locked_by::LockedBy;

0 commit comments

Comments
 (0)