Skip to content

Commit d4b51d5

Browse files
authored
Merge pull request raspberrypi#780 from wedsonaf/static-ref
rust: add `StaticRef`
2 parents e0326eb + 0164a14 commit d4b51d5

File tree

3 files changed

+83
-4
lines changed

3 files changed

+83
-4
lines changed

rust/kernel/sync.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ mod seqlock;
3636
pub mod smutex;
3737
mod spinlock;
3838

39-
pub use arc::{Ref, RefBorrow, UniqueRef};
39+
pub use arc::{new_refcount, Ref, RefBorrow, StaticRef, UniqueRef};
4040
pub use condvar::CondVar;
4141
pub use guard::{Guard, Lock, LockFactory, LockInfo, LockIniter, ReadLock, WriteLock};
4242
pub use locked_by::LockedBy;

rust/kernel/sync/arc.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@ impl<T> Ref<T> {
8686

8787
// INVARIANT: The refcount is initialised to a non-zero value.
8888
let value = RefInner {
89-
// SAFETY: Just an FFI call that returns a `refcount_t` initialised to 1.
90-
refcount: Opaque::new(unsafe { bindings::REFCOUNT_INIT(1) }),
89+
refcount: Opaque::new(new_refcount()),
9190
data: contents,
9291
};
9392
// SAFETY: `inner` is writable and properly aligned.
@@ -501,3 +500,83 @@ impl<T: ?Sized> DerefMut for UniqueRef<T> {
501500
unsafe { &mut self.inner.ptr.as_mut().data }
502501
}
503502
}
503+
504+
/// Allows the creation of "reference-counted" globals.
505+
///
506+
/// This is achieved by biasing the refcount with +1, which ensures that the count never drops back
507+
/// to zero (unless buggy unsafe code incorrectly decrements without owning an increment) and
508+
/// therefore also ensures that `drop` is never called.
509+
///
510+
/// # Examples
511+
///
512+
/// ```
513+
/// use kernel::sync::{Ref, RefBorrow, StaticRef};
514+
///
515+
/// const VALUE: u32 = 10;
516+
/// static SR: StaticRef<u32> = StaticRef::new(VALUE);
517+
///
518+
/// fn takes_ref_borrow(v: RefBorrow<'_, u32>) {
519+
/// assert_eq!(*v, VALUE);
520+
/// }
521+
///
522+
/// fn takes_ref(v: Ref<u32>) {
523+
/// assert_eq!(*v, VALUE);
524+
/// }
525+
///
526+
/// takes_ref_borrow(SR.as_ref_borrow());
527+
/// takes_ref(SR.as_ref_borrow().into());
528+
/// ```
529+
pub struct StaticRef<T: ?Sized> {
530+
inner: RefInner<T>,
531+
}
532+
533+
// SAFETY: A `StaticRef<T>` is a `Ref<T>` declared statically, so we just use the same criteria for
534+
// making it `Sync`.
535+
unsafe impl<T: ?Sized + Sync + Send> Sync for StaticRef<T> {}
536+
537+
impl<T> StaticRef<T> {
538+
/// Creates a new instance of a static "ref-counted" object.
539+
pub const fn new(data: T) -> Self {
540+
// INVARIANT: The refcount is initialised to a non-zero value.
541+
Self {
542+
inner: RefInner {
543+
refcount: Opaque::new(new_refcount()),
544+
data,
545+
},
546+
}
547+
}
548+
}
549+
550+
impl<T: ?Sized> StaticRef<T> {
551+
/// Creates a [`RefBorrow`] instance from the given static object.
552+
///
553+
/// This requires a `'static` lifetime so that it can guarantee that the underlyling object
554+
/// remains valid and is effectively pinned.
555+
pub fn as_ref_borrow(&'static self) -> RefBorrow<'static, T> {
556+
// SAFETY: The static lifetime guarantees that the object remains valid. And the shared
557+
// reference guarantees that no mutable references exist.
558+
unsafe { RefBorrow::new(NonNull::from(&self.inner)) }
559+
}
560+
}
561+
562+
/// Creates, from a const context, a new instance of `struct refcount_struct` with a refcount of 1.
563+
///
564+
/// ```
565+
/// # // The test below is meant to ensure that `new_refcount` (which is const) mimics
566+
/// # // `REFCOUNT_INIT`, which is written in C and thus can't be used in a const context.
567+
/// # // TODO: Once `#[test]` is working, move this to a test and make `new_refcount` private.
568+
/// # use kernel::bindings;
569+
/// # // SAFETY: Just an FFI call that returns a `refcount_t` initialised to 1.
570+
/// # let bindings::refcount_struct {
571+
/// # refs: bindings::atomic_t { counter: a },
572+
/// # } = unsafe { bindings::REFCOUNT_INIT(1) };
573+
/// # let bindings::refcount_struct {
574+
/// # refs: bindings::atomic_t { counter: b },
575+
/// # } = kernel::sync::new_refcount();
576+
/// # assert_eq!(a, b);
577+
/// ```
578+
pub const fn new_refcount() -> bindings::refcount_struct {
579+
bindings::refcount_struct {
580+
refs: bindings::atomic_t { counter: 1 },
581+
}
582+
}

rust/kernel/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ pub struct Opaque<T>(MaybeUninit<UnsafeCell<T>>);
286286

287287
impl<T> Opaque<T> {
288288
/// Creates a new opaque value.
289-
pub fn new(value: T) -> Self {
289+
pub const fn new(value: T) -> Self {
290290
Self(MaybeUninit::new(UnsafeCell::new(value)))
291291
}
292292

0 commit comments

Comments
 (0)