Skip to main content

aligned_vmem/
reservation_parts.rs

1/// The components returned by [`Reservation::into_reservation_parts`](crate::Reservation::into_reservation_parts).
2///
3/// A named structure (instead of a raw tuple) prevents the footgun of
4/// accidentally swapping the `len` and `align` fields, which would be
5/// undefined behavior on the native backend and cause leaks or crashes
6/// on the Unix backend.
7///
8/// `ReservationParts::new` closes the `release_parts` round-trip (release a
9/// reservation you only have the parts for). Reconstructing a full
10/// `Reservation` via `from_raw_parts` additionally requires the usable `base`,
11/// `len`, and `granted_huge` fields, which the caller must record separately —
12/// `ReservationParts` alone is insufficient whenever the reservation was
13/// over-reserved for alignment or when huge-page status must be preserved.
14/// If you omit `granted_huge`, the reconstructed reservation will incorrectly
15/// report `is_huge() == false` even if the original reservation used huge pages.
16///
17/// **This struct holds the ONLY information that can free the underlying OS
18/// reservation** (task #1213/L3) — a plain `drop` of a `ReservationParts`
19/// (letting it go out of scope without ever calling
20/// [`release_parts`](crate::api::release_parts) or reconstructing a full
21/// [`Reservation`](crate::Reservation)) silently leaks the mapping: this
22/// struct has no `Drop` impl of its own. `#[must_use]` here catches an
23/// ACCIDENTALLY discarded ownership token (e.g. a call to
24/// [`Reservation::into_reservation_parts`](crate::Reservation::into_reservation_parts)
25/// whose result is never bound to anything) at compile time; it does not
26/// and cannot prevent a DELIBERATE leak (e.g. binding the result to `_` or
27/// storing it and then dropping it later without acting on it).
28#[must_use = "dropping `ReservationParts` leaks the reservation — release it \
29              via `release_parts`, or reconstruct a `Reservation` via \
30              `Reservation::from_raw_parts` and let that drop instead"]
31#[non_exhaustive]
32#[derive(Debug, PartialEq, Eq)]
33pub struct ReservationParts {
34    /// The base pointer of the reservation (from [`Reservation::reservation_ptr`](crate::Reservation::reservation_ptr)).
35    pub ptr: *mut u8,
36    /// The length of the reservation in bytes (from [`Reservation::reservation_len`](crate::Reservation::reservation_len)).
37    pub len: usize,
38    /// The alignment requested at reservation time.
39    pub align: usize,
40}
41
42impl ReservationParts {
43    /// Construct a `ReservationParts` from its component fields.
44    ///
45    /// This closes the `release_parts` round-trip (release a reservation you
46    /// only have the parts for). Reconstructing a full `Reservation` via
47    /// `from_raw_parts` additionally requires the usable `base`, `len`, and
48    /// `granted_huge` fields, which the caller must record separately —
49    /// `ReservationParts` alone is insufficient whenever the reservation was
50    /// over-reserved for alignment or when huge-page status must be preserved.
51    ///
52    /// No message-less `#[must_use]` on this function itself (task
53    /// #1213/L3): it returns `Self`, and the type now carries its own
54    /// `#[must_use]` with a leak-specific message — a redundant message-less
55    /// attribute on top of that is exactly what clippy's `double_must_use`
56    /// lint rejects.
57    #[inline]
58    pub const fn new(ptr: *mut u8, len: usize, align: usize) -> Self {
59        Self { ptr, len, align }
60    }
61
62    /// Convert this struct back into a raw tuple compatible with [`release`](crate::api::release).
63    ///
64    /// This method exists only for backwards compatibility with code that
65    /// already uses the tuple form. New code should use [`release_parts`](crate::api::release_parts) instead.
66    #[must_use]
67    #[inline]
68    pub const fn as_tuple(self) -> (*mut u8, usize, usize) {
69        (self.ptr, self.len, self.align)
70    }
71}
72
73// SAFETY (Send): `ptr` describes the same exclusively-owned OS reservation
74// `Reservation` itself is `Send` for (see the identical argument on
75// `unsafe impl Send for Reservation` in `reservation.rs`) — moving a
76// `ReservationParts` to another thread moves ownership of every byte it
77// describes, leaving no aliasing on the origin thread. Every operation that
78// dereferences `ptr` is already `unsafe`, so this impl grants no new unsafe
79// capability, only cross-thread ownership transfer of the token itself.
80// Deliberately NOT `Sync` (task #1257/OH13-F4): `&ReservationParts` would
81// hand out `ptr` — a live `*mut u8` — to a second reader while the first
82// still holds the token, the same reason `Reservation` withholds `Sync` too.
83unsafe impl Send for ReservationParts {}