Skip to content

Commit ad86bf9

Browse files
authored
Merge pull request #468 from wedsonaf/ref-reservation
rust: add `UniqueRef`.
2 parents 9027821 + c55a9f5 commit ad86bf9

File tree

2 files changed

+159
-18
lines changed

2 files changed

+159
-18
lines changed

rust/kernel/sync/arc.rs

Lines changed: 158 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use core::{
2525
cell::UnsafeCell,
2626
convert::{AsRef, TryFrom},
2727
marker::{PhantomData, Unsize},
28-
mem::ManuallyDrop,
29-
ops::Deref,
28+
mem::{ManuallyDrop, MaybeUninit},
29+
ops::{Deref, DerefMut},
3030
pin::Pin,
3131
ptr::{self, NonNull},
3232
};
@@ -75,17 +75,10 @@ unsafe impl<T: ?Sized + Sync + Send> Sync for Ref<T> {}
7575
impl<T> Ref<T> {
7676
/// Constructs a new reference counted instance of `T`.
7777
pub fn try_new(contents: T) -> Result<Self> {
78-
Self::try_new_and_init(contents, |_| {})
79-
}
80-
81-
/// Constructs a new reference counted instance of `T` and calls the initialisation function.
82-
///
83-
/// This is useful because it provides a mutable reference to `T` at its final location.
84-
pub fn try_new_and_init<U: FnOnce(Pin<&mut T>)>(contents: T, init: U) -> Result<Self> {
8578
let layout = Layout::new::<RefInner<T>>();
8679
// SAFETY: The layout size is guaranteed to be non-zero because `RefInner` contains the
8780
// reference count.
88-
let mut inner = NonNull::new(unsafe { alloc(layout) })
81+
let inner = NonNull::new(unsafe { alloc(layout) })
8982
.ok_or(Error::ENOMEM)?
9083
.cast::<RefInner<T>>();
9184

@@ -98,18 +91,18 @@ impl<T> Ref<T> {
9891
// SAFETY: `inner` is writable and properly aligned.
9992
unsafe { inner.as_ptr().write(value) };
10093

101-
// SAFETY: By the invariant, `RefInner` is pinned and `T` is also pinned.
102-
let pinned = unsafe { Pin::new_unchecked(&mut inner.as_mut().data) };
103-
104-
// INVARIANT: The only places where `&mut T` is available are here, which is explicitly
105-
// pinned, and in `drop`. Both are compatible with the pin requirements.
106-
init(pinned);
107-
10894
// SAFETY: We just created `inner` with a reference count of 1, which is owned by the new
10995
// `Ref` object.
11096
Ok(unsafe { Self::from_inner(inner) })
11197
}
11298

99+
/// Constructs a new reference counted instance of `T` and calls the initialisation function.
100+
///
101+
/// This is useful because it provides a mutable reference to `T` at its final location.
102+
pub fn try_new_and_init<U: FnOnce(Pin<&mut T>)>(contents: T, init: U) -> Result<Self> {
103+
Ok(UniqueRef::try_new(contents)?.pin_init_and_share(init))
104+
}
105+
113106
/// Deconstructs a [`Ref`] object into a `usize`.
114107
///
115108
/// It can be reconstructed once via [`Ref::from_usize`].
@@ -306,6 +299,12 @@ impl<T> TryFrom<Vec<T>> for Ref<[T]> {
306299
}
307300
}
308301

302+
impl<T: ?Sized> From<UniqueRef<T>> for Ref<T> {
303+
fn from(item: UniqueRef<T>) -> Self {
304+
item.inner
305+
}
306+
}
307+
309308
/// A borrowed [`Ref`] with manually-managed lifetime.
310309
///
311310
/// # Invariants
@@ -337,3 +336,145 @@ impl<T: ?Sized> Deref for RefBorrow<T> {
337336
self.inner_ref.deref()
338337
}
339338
}
339+
340+
/// A refcounted object that is known to have a refcount of 1.
341+
///
342+
/// It is mutable and can be converted to a [`Ref`] so that it can be shared.
343+
///
344+
/// # Invariants
345+
///
346+
/// `inner` always has a reference count of 1.
347+
///
348+
/// # Examples
349+
///
350+
/// In the following example, we make changes to the inner object before turning it into a
351+
/// `Ref<Test>` object (after which point, it cannot be mutated directly). Note that `x.into()`
352+
/// cannot fail.
353+
///
354+
/// ```
355+
/// # use kernel::prelude::*;
356+
/// use kernel::sync::{Ref, UniqueRef};
357+
///
358+
/// struct Example {
359+
/// a: u32,
360+
/// b: u32,
361+
/// }
362+
///
363+
/// fn test() -> Result<Ref<Example>> {
364+
/// let mut x = UniqueRef::try_new(Example { a: 10, b: 20 })?;
365+
/// x.a += 1;
366+
/// x.b += 1;
367+
/// Ok(x.into())
368+
/// }
369+
/// ```
370+
///
371+
/// In the following example we first allocate memory for a ref-counted `Example` but we don't
372+
/// initialise it on allocation. We do initialise it later with a call to [`UniqueRef::write`],
373+
/// followed by a conversion to `Ref<Example>`. This is particularly useful when allocation happens
374+
/// in one context (e.g., sleepable) and initialisation in another (e.g., atomic):
375+
///
376+
/// ```
377+
/// # use kernel::prelude::*;
378+
/// use kernel::sync::{Ref, UniqueRef};
379+
///
380+
/// struct Example {
381+
/// a: u32,
382+
/// b: u32,
383+
/// }
384+
///
385+
/// fn test2() -> Result<Ref<Example>> {
386+
/// let x = UniqueRef::try_new_uninit()?;
387+
/// Ok(x.write(Example { a: 10, b: 20 }).into())
388+
/// }
389+
/// ```
390+
///
391+
/// In the last example below, the caller gets a pinned instance of `Example` while converting to
392+
/// `Ref<Example>`; this is useful in scenarios where one needs a pinned reference during
393+
/// initialisation, for example, when initialising fields that are wrapped in locks.
394+
///
395+
/// ```
396+
/// # use kernel::prelude::*;
397+
/// use kernel::sync::{Ref, UniqueRef};
398+
///
399+
/// struct Example {
400+
/// a: u32,
401+
/// b: u32,
402+
/// }
403+
///
404+
/// fn test2() -> Result<Ref<Example>> {
405+
/// let mut x = UniqueRef::try_new(Example { a: 10, b: 20 })?;
406+
/// // We can modify `pinned` because it is `Unpin`.
407+
/// Ok(x.pin_init_and_share(|mut pinned| pinned.a += 1))
408+
/// }
409+
/// ```
410+
pub struct UniqueRef<T: ?Sized> {
411+
inner: Ref<T>,
412+
}
413+
414+
impl<T> UniqueRef<T> {
415+
/// Tries to allocate a new [`UniqueRef`] instance.
416+
pub fn try_new(value: T) -> Result<Self> {
417+
Ok(Self {
418+
// INVARIANT: The newly-created object has a ref-count of 1.
419+
inner: Ref::try_new(value)?,
420+
})
421+
}
422+
423+
/// Tries to allocate a new [`UniqueRef`] instance whose contents are not initialised yet.
424+
pub fn try_new_uninit() -> Result<UniqueRef<MaybeUninit<T>>> {
425+
Ok(UniqueRef::<MaybeUninit<T>> {
426+
// INVARIANT: The newly-created object has a ref-count of 1.
427+
inner: Ref::try_new(MaybeUninit::uninit())?,
428+
})
429+
}
430+
}
431+
432+
impl<T: ?Sized> UniqueRef<T> {
433+
/// Converts a [`UniqueRef<T>`] into a [`Ref<T>`].
434+
///
435+
/// It allows callers to get a `Pin<&mut T>` that they can use to initialise the inner object
436+
/// just before it becomes shareable.
437+
pub fn pin_init_and_share<U: FnOnce(Pin<&mut T>)>(mut self, init: U) -> Ref<T> {
438+
let inner = self.deref_mut();
439+
440+
// SAFETY: By the `Ref` invariant, `RefInner` is pinned and `T` is also pinned.
441+
let pinned = unsafe { Pin::new_unchecked(inner) };
442+
443+
// INVARIANT: The only places where `&mut T` is available are here (where it is explicitly
444+
// pinned, i.e. implementations of `init` will see a Pin<&mut T>), and in `Ref::drop`. Both
445+
// are compatible with the pin requirements of the invariants of `Ref`.
446+
init(pinned);
447+
448+
self.into()
449+
}
450+
}
451+
452+
impl<T> UniqueRef<MaybeUninit<T>> {
453+
/// Converts a `UniqueRef<MaybeUninit<T>>` into a `UniqueRef<T>` by writing a value into it.
454+
pub fn write(mut self, value: T) -> UniqueRef<T> {
455+
self.deref_mut().write(value);
456+
let inner = ManuallyDrop::new(self).inner.ptr;
457+
UniqueRef {
458+
// SAFETY: The new `Ref` is taking over `ptr` from `self.inner` (which won't be
459+
// dropped). The types are compatible because `MaybeUninit<T>` is compatible with `T`.
460+
inner: unsafe { Ref::from_inner(inner.cast()) },
461+
}
462+
}
463+
}
464+
465+
impl<T: ?Sized> Deref for UniqueRef<T> {
466+
type Target = T;
467+
468+
fn deref(&self) -> &Self::Target {
469+
self.inner.deref()
470+
}
471+
}
472+
473+
impl<T: ?Sized> DerefMut for UniqueRef<T> {
474+
fn deref_mut(&mut self) -> &mut Self::Target {
475+
// SAFETY: By the `Ref` type invariant, there is necessarily a reference to the object, so
476+
// it is safe to dereference it. Additionally, we know there is only one reference when
477+
// it's inside a `UniqueRef`, so it is safe to get a mutable reference.
478+
unsafe { &mut self.inner.ptr.as_mut().data }
479+
}
480+
}

rust/kernel/sync/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ mod locked_by;
3131
mod mutex;
3232
mod spinlock;
3333

34-
pub use arc::{Ref, RefBorrow};
34+
pub use arc::{Ref, RefBorrow, UniqueRef};
3535
pub use condvar::CondVar;
3636
pub use guard::{Guard, Lock};
3737
pub use locked_by::LockedBy;

0 commit comments

Comments
 (0)