Skip to main content

miniextendr_api/
preserved.rs

1//! [`Preserved`]: an owned GC root for one R object, on R's precious list.
2
3use std::marker::PhantomData;
4
5use crate::SEXP;
6use crate::into_r::IntoR;
7
8/// An owned GC root for one R object: `R_PreserveObject` when made,
9/// `R_ReleaseObject` when dropped.
10///
11/// Hold one where a Rust value keeps an R object across allocations or past
12/// the `.Call()` that made it, such as a field of a struct that lives in an
13/// [`ExternalPtr`](struct@crate::ExternalPtr) and drops with its finalizer.
14/// The object stays reachable for as long as the handle lives, whatever the
15/// drop order. [`ProtectScope`](crate::ProtectScope) and
16/// [`OwnedProtect`](crate::OwnedProtect) use R's protect stack instead, which
17/// unwinds in order and ends with the call.
18///
19/// ```ignore
20/// struct Fit {
21///     data: Preserved, // the data frame the model was fitted on
22/// }
23///
24/// let fit = Fit { data: Preserved::new(df) };
25/// // Any number of allocations and `.Call()`s later:
26/// let n = fit.data.len();
27/// ```
28///
29/// - [`Clone`] preserves the object again. Each handle releases its own entry,
30///   so clones drop in any order.
31/// - The handle dereferences to its [`SEXP`], so [`SexpExt`](crate::SexpExt)
32///   methods apply to it. A `SEXP` copied out of it (`*handle`,
33///   [`as_sexp`](Self::as_sexp)) stays rooted only while the handle lives.
34/// - [`IntoR`] releases the root and hands the object to the caller: return
35///   the handle from a `#[miniextendr]` function to return the object.
36/// - `!Send + !Sync`: the precious list is R main-thread state. Make, clone
37///   and drop the handle on R's main thread.
38///
39/// # Cost
40///
41/// R keeps the precious list as one linked list. Preserving conses a cell onto
42/// its head, an allocation. Releasing searches the list from the head, one step
43/// per object preserved after this one and not yet released. Starting R with
44/// the environment variable `R_HASH_PRECIOUS` set splits the list into 1069
45/// hashed buckets.
46///
47/// That suits a few long-lived objects. For many roots, or roots released in
48/// no particular order, use a [`ProtectPool`](crate::ProtectPool): it preserves
49/// one list and inserts and releases in O(1), and it roots every
50/// [`ExternalPtr`](struct@crate::ExternalPtr), because a `Vec` of handles
51/// dropped front to back makes each release scan the whole precious list.
52/// [`RefCountedArena`](crate::RefCountedArena) does the same with a count per
53/// object. Within one call, the protect stack ([`ProtectScope`]) allocates
54/// nothing.
55///
56/// [`ProtectScope`]: crate::ProtectScope
57pub struct Preserved {
58    sexp: SEXP,
59    /// `!Send + !Sync`: preserving and releasing edit R's precious list.
60    _not_send: PhantomData<*mut ()>,
61}
62
63impl Preserved {
64    /// Convert `value` to an R object and preserve it.
65    ///
66    /// A [`SEXP`] converts to itself, so `Preserved::new(sexp)` roots an
67    /// existing object. Rooting is immediate: nothing allocates between the
68    /// conversion and `R_PreserveObject`, which keeps its argument reachable
69    /// while it allocates its cell, so a fresh, unprotected object is safe to
70    /// pass. Call it on R's main thread.
71    pub fn new(value: impl IntoR) -> Self {
72        let sexp = value.into_sexp();
73        // SAFETY: `into_sexp` just ran on R's main thread and returned a valid
74        // SEXP; rooting is immediate (see above).
75        unsafe { crate::sys::R_PreserveObject(sexp) };
76        Self {
77            sexp,
78            _not_send: PhantomData,
79        }
80    }
81
82    /// The preserved object, rooted for as long as this handle lives.
83    #[inline]
84    pub fn as_sexp(&self) -> SEXP {
85        self.sexp
86    }
87
88    /// Release the root and return the object, now unprotected: the caller
89    /// takes it over. `R_ReleaseObject` does not allocate, so returning the
90    /// object straight to R (the `.Call()` return) is safe.
91    fn release(self) -> SEXP {
92        let sexp = self.sexp;
93        // SAFETY: `!Send` keeps us on the thread that preserved it. Release the
94        // entry added in `new`, and skip `Drop` so it is not released twice.
95        unsafe { crate::sys::R_ReleaseObject(sexp) };
96        std::mem::forget(self);
97        sexp
98    }
99}
100
101impl Clone for Preserved {
102    /// Preserve the object again, for a handle with its own entry.
103    fn clone(&self) -> Self {
104        Self::new(self.sexp)
105    }
106}
107
108impl Drop for Preserved {
109    fn drop(&mut self) {
110        // SAFETY: `!Send` keeps the drop on the thread that preserved it.
111        // Release removes the entry added in `new` (precious-list entries
112        // stack, so other handles to the object keep theirs) and allocates
113        // nothing.
114        unsafe { crate::sys::R_ReleaseObject(self.sexp) };
115    }
116}
117
118impl std::ops::Deref for Preserved {
119    type Target = SEXP;
120
121    #[inline]
122    fn deref(&self) -> &SEXP {
123        &self.sexp
124    }
125}
126
127impl IntoR for Preserved {
128    type Error = std::convert::Infallible;
129
130    fn try_into_sexp(self) -> Result<SEXP, Self::Error> {
131        Ok(self.release())
132    }
133
134    unsafe fn try_into_sexp_unchecked(self) -> Result<SEXP, Self::Error> {
135        Ok(self.release())
136    }
137
138    #[inline]
139    fn into_sexp(self) -> SEXP {
140        self.release()
141    }
142}
143
144impl std::fmt::Debug for Preserved {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        f.debug_tuple("Preserved").field(&self.sexp).finish()
147    }
148}