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}