Skip to content

Commit 68d663d

Browse files
committed
rust: add RefReservation.
This allows a ref-counted object to be allocated but initialised later. It is useful, for example, to allocate in one context (e.g., sleepable context) and initialise in a different context (e.g., atomic context). It is the last remaining feature before we can remove all usages of `Arc<T>` from Binder. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 0b008f8 commit 68d663d

File tree

2 files changed

+46
-4
lines changed

2 files changed

+46
-4
lines changed

rust/kernel/sync/arc.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use core::{
2525
cell::UnsafeCell,
2626
convert::{AsRef, TryFrom},
2727
marker::{PhantomData, Unsize},
28-
mem::ManuallyDrop,
28+
mem::{ManuallyDrop, MaybeUninit},
2929
ops::Deref,
3030
pin::Pin,
3131
ptr::{self, NonNull},
@@ -107,8 +107,8 @@ impl<T> Ref<T> {
107107
// SAFETY: By the invariant, `RefInner` is pinned and `T` is also pinned.
108108
let pinned = unsafe { Pin::new_unchecked(&mut inner.as_mut().data) };
109109

110-
// INVARIANT: The only places where `&mut T` is available are here, which is explicitly
111-
// pinned, and in `drop`. Both are compatible with the pin requirements.
110+
// INVARIANT: The only places where `&mut T` is available are in constructors, which are
111+
// explicitly pinned, and in `drop`. Both are compatible with the pin requirements.
112112
init(pinned);
113113

114114
// SAFETY: We just created `inner` with a reference count of 1, which is owned by the new
@@ -343,3 +343,45 @@ impl<T: ?Sized> Deref for RefBorrow<T> {
343343
self.inner_ref.deref()
344344
}
345345
}
346+
347+
/// An allocated but not yet initialised ref-counted object.
348+
pub struct RefReservation<T> {
349+
mem: Ref<MaybeUninit<T>>,
350+
}
351+
352+
impl<T> RefReservation<T> {
353+
/// Allocates memory for a [`Ref<T>`] instance.
354+
///
355+
/// The instance is not initialised yet.
356+
pub fn try_new() -> Result<Self> {
357+
Ok(Self {
358+
mem: Ref::try_new(MaybeUninit::<T>::uninit())?,
359+
})
360+
}
361+
362+
/// Converts a reservation into a real ref-counted object by initialising it.
363+
pub fn commit(self, data: T) -> Ref<T> {
364+
let mut inner = ManuallyDrop::new(self).mem.ptr;
365+
// SAFETY: `inner.data` is writable and properly aligned because it was allocated for a
366+
// `MaybeUninit<T>`, which is compatible with `T`.
367+
unsafe { inner.as_mut().data.as_mut_ptr().write(data) };
368+
// SAFETY: The new `Ref` is taking over `inner` from `self.mem` (which won't be dropped).
369+
unsafe { Ref::<T>::from_inner(inner.cast()) }
370+
}
371+
372+
/// Converts a reservation into a real ref-counted object by initialising it with the given
373+
/// value and callback.
374+
///
375+
/// This is useful because it provides a mutable reference to `T` at its final location.
376+
pub fn commit_and_init<U: FnOnce(Pin<&mut T>)>(self, data: T, init: U) -> Ref<T> {
377+
let mut ret = self.commit(data);
378+
379+
// SAFETY: By the invariant, `RefInner` is pinned and `T` is also pinned.
380+
let pinned = unsafe { Pin::new_unchecked(&mut ret.ptr.as_mut().data) };
381+
382+
// INVARIANT: The only places where `&mut T` is available are in constructors, which are
383+
// explicitly pinned, and in `drop`. Both are compatible with the pin requirements.
384+
init(pinned);
385+
ret
386+
}
387+
}

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, RefReservation};
3535
pub use condvar::CondVar;
3636
pub use guard::{Guard, Lock};
3737
pub use locked_by::LockedBy;

0 commit comments

Comments
 (0)