On Mon, Aug 11, 2025 at 9:29 AM Andreas Hindborg <a.hindborg@xxxxxxxxxx> wrote: > > "Tamir Duberstein" <tamird@xxxxxxxxx> writes: > > > Add `Guard::{insert,reserve}` and `Guard::{insert,reserve}_limit`, which > > are akin to `__xa_{alloc,insert}` in C. > > > > Note that unlike `xa_reserve` which only ensures that memory is > > allocated, the semantics of `Reservation` are stricter and require > > precise management of the reservation. Indices which have been reserved > > can still be overwritten with `Guard::store`, which allows for C-like > > semantics if desired. > > > > `__xa_cmpxchg_raw` is exported to facilitate the semantics described > > above. > > > > Tested-by: Janne Grunau <j@xxxxxxxxxx> > > Reviewed-by: Janne Grunau <j@xxxxxxxxxx> > > Signed-off-by: Tamir Duberstein <tamird@xxxxxxxxx> > > <cut> > > > + /// Stores an element somewhere in the given range of indices. > > + /// > > + /// On success, takes ownership of `ptr`. > > + /// > > + /// On failure, ownership returns to the caller. > > + /// > > + /// # Safety > > + /// > > + /// `ptr` must be `NULL` or have come from a previous call to `T::into_foreign`. > > + unsafe fn alloc( > > > The naming of this method in C is confusing. Could we call it > insert_limit_raw on the Rust side? I think I prefer to hew close to the C naming. Is there prior art where Rust names deviate from C names? > Even though this is private, I think we should also document that the > effect of inserting NULL is to reserve the entry. 👍 > > + &mut self, > > + limit: impl ops::RangeBounds<u32>, > > + ptr: *mut T::PointedTo, > > + gfp: alloc::Flags, > > + ) -> Result<usize> { > > + // NB: `xa_limit::{max,min}` are inclusive. > > + let limit = bindings::xa_limit { > > + max: match limit.end_bound() { > > + ops::Bound::Included(&end) => end, > > + ops::Bound::Excluded(&end) => end - 1, > > + ops::Bound::Unbounded => u32::MAX, > > + }, > > + min: match limit.start_bound() { > > + ops::Bound::Included(&start) => start, > > + ops::Bound::Excluded(&start) => start + 1, > > + ops::Bound::Unbounded => 0, > > + }, > > + }; > > + > > + let mut index = u32::MAX; > > + > > + // SAFETY: > > + // - `self.xa` is always valid by the type invariant. > > + // - `self.xa` was initialized with `XA_FLAGS_ALLOC` or `XA_FLAGS_ALLOC1`. > > + // > > + // INVARIANT: `ptr` is either `NULL` or came from `T::into_foreign`. > > + match unsafe { > > + bindings::__xa_alloc( > > + self.xa.xa.get(), > > + &mut index, > > + ptr.cast(), > > + limit, > > + gfp.as_raw(), > > + ) > > + } { > > + 0 => Ok(to_usize(index)), > > + errno => Err(Error::from_errno(errno)), > > + } > > + } > > + > > + /// Allocates an entry somewhere in the array. > > Should we rephrase this to match `alloc`? > > Stores an entry somewhere in the given range of indices. 👍 > <cut> > > > +impl<T: ForeignOwnable> Reservation<'_, T> { > > + /// Returns the index of the reservation. > > + pub fn index(&self) -> usize { > > + self.index > > + } > > + > > + /// Replaces the reserved entry with the given entry. > > + /// > > + /// # Safety > > + /// > > + /// `ptr` must be `NULL` or have come from a previous call to `T::into_foreign`. > > We should document the effect of replacing with NULL. 👍 > Best regards, > Andreas Hindborg > >