extendr_api/
ownership.rs

1//! Maintain ownership of R objects.
2//!
3//! This provides the functions `protect` and `unprotect`.
4//! A single preserved vector holds ownership of all protected objects.
5//!
6//! Objects are reference counted, so multiple calls are possible,
7//! unlike `R_PreserveObject`.
8//!
9//! This module exports two functions, `protect(sexp)` and `unprotect(sexp)`.
10
11use once_cell::sync::Lazy;
12use std::collections::hash_map::{Entry, HashMap};
13use std::sync::Mutex;
14
15use extendr_ffi::{
16    R_NilValue, R_PreserveObject, R_ReleaseObject, R_xlen_t, Rf_allocVector, Rf_protect,
17    Rf_unprotect, LENGTH, SET_VECTOR_ELT, SEXP, SEXPTYPE, VECTOR_ELT,
18};
19
20mod send_sexp {
21    //! Provide a wrapper around R's pointer type `SEXP` that is `Send`.
22    //!
23    //! This can lead to soundness issues, therefore accessing the `SEXP` has
24    //! to happen through the unsafe method [`SendSEXP::inner`].
25    //!
26    use extendr_ffi::SEXP;
27
28    /// A wrapper around R's pointer type `SEXP` that is `Send`.
29    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
30    pub struct SendSEXP(SEXP);
31
32    impl From<SEXP> for SendSEXP {
33        fn from(value: SEXP) -> Self {
34            Self(value)
35        }
36    }
37
38    // Allows SendSEXP to be sent between threads even though unsafe
39    // Requires that the SEXP is not accessed concurrently.
40    unsafe impl Send for SendSEXP {}
41
42    impl SendSEXP {
43        /// Get the inner `SEXP`
44        pub unsafe fn inner(&self) -> SEXP {
45            self.0
46        }
47    }
48}
49
50use self::send_sexp::SendSEXP;
51
52static OWNERSHIP: Lazy<Mutex<Ownership>> = Lazy::new(|| Mutex::new(Ownership::new()));
53
54pub(crate) unsafe fn protect(sexp: SEXP) {
55    let mut own = OWNERSHIP.lock().expect("protect failed");
56    own.protect(sexp);
57}
58
59pub(crate) unsafe fn unprotect(sexp: SEXP) {
60    let mut own = OWNERSHIP.lock().expect("unprotect failed");
61    own.unprotect(sexp);
62}
63
64pub const INITIAL_PRESERVATION_SIZE: usize = 100000;
65pub const EXTRA_PRESERVATION_SIZE: usize = 100000;
66
67// `Object` is a manual reference counting mechanism that is used for each SEXP.
68// `refcount` is the number of times the SEXP is accessed.
69// `index` is the index of the SEXP in the preservation vector.
70#[derive(Debug)]
71struct Object {
72    refcount: usize,
73    index: usize,
74}
75
76// A reference counted object with an index in the preservation vector.
77#[derive(Debug)]
78struct Ownership {
79    // A growable vector containing all owned objects.
80    preservation: SendSEXP,
81
82    // An incrementing count of objects through the vector.
83    cur_index: usize,
84
85    // The size of the vector.
86    max_index: usize,
87
88    // A hash map from SEXP address to object.
89    objects: HashMap<SendSEXP, Object>,
90}
91
92impl Ownership {
93    fn new() -> Self {
94        unsafe {
95            let preservation =
96                Rf_allocVector(SEXPTYPE::VECSXP, INITIAL_PRESERVATION_SIZE as R_xlen_t);
97            R_PreserveObject(preservation);
98            Ownership {
99                preservation: preservation.into(),
100                cur_index: 0,
101                max_index: INITIAL_PRESERVATION_SIZE,
102                objects: HashMap::with_capacity(INITIAL_PRESERVATION_SIZE),
103            }
104        }
105    }
106
107    // Garbage collect the tracking structures.
108    unsafe fn garbage_collect(&mut self) {
109        let new_size = self.cur_index * 2 + EXTRA_PRESERVATION_SIZE;
110        let new_sexp = Rf_allocVector(SEXPTYPE::VECSXP, new_size as R_xlen_t);
111        R_PreserveObject(new_sexp);
112        let old_sexp = self.preservation.inner();
113
114        let mut new_objects = HashMap::with_capacity(new_size);
115
116        // copy non-null elements to new vector and hashmap.
117        let mut j = 0;
118        for (addr, object) in self.objects.iter() {
119            if object.refcount != 0 {
120                SET_VECTOR_ELT(new_sexp, j as R_xlen_t, addr.inner());
121                new_objects.insert(
122                    addr.clone(),
123                    Object {
124                        refcount: object.refcount,
125                        index: j,
126                    },
127                );
128                j += 1;
129            }
130        }
131
132        R_ReleaseObject(old_sexp);
133        self.preservation = (new_sexp).into();
134        self.cur_index = j;
135        self.max_index = new_size;
136        self.objects = new_objects;
137    }
138
139    unsafe fn protect(&mut self, sexp: SEXP) {
140        // This protects the SEXP. Is this necessary?
141        // Because the Ownership object already protects an SEXP in the `preservation` field.
142        // The new `sexp` is inserted into the preservation list via `SET_VECTOR_ELT` below.
143        // If list is protected then so are all of its elements.
144        //
145        // > Protecting an R object automatically protects all the R objects
146        // > pointed to in the corresponding SEXPREC, for example all elements
147        // > of a protected list are automatically protected." 5.9.1
148        Rf_protect(sexp);
149
150        if self.cur_index == self.max_index {
151            self.garbage_collect();
152        }
153
154        let send_sexp = sexp.into();
155        let Ownership {
156            ref mut preservation,
157            ref mut cur_index,
158            ref mut max_index,
159            ref mut objects,
160        } = *self;
161
162        let mut entry = objects.entry(send_sexp);
163        let preservation_sexp = preservation.inner();
164        match entry {
165            Entry::Occupied(ref mut occupied) => {
166                if occupied.get().refcount == 0 {
167                    // Address re-used - re-set the sexp.
168                    SET_VECTOR_ELT(preservation_sexp, occupied.get().index as R_xlen_t, sexp);
169                }
170                occupied.get_mut().refcount += 1;
171            }
172            Entry::Vacant(vacant) => {
173                let index = *cur_index;
174                SET_VECTOR_ELT(preservation_sexp, index as R_xlen_t, sexp);
175                *cur_index += 1;
176                assert!(index != *max_index);
177                let refcount = 1;
178                vacant.insert(Object { refcount, index });
179            }
180        }
181
182        Rf_unprotect(1);
183    }
184
185    pub unsafe fn unprotect(&mut self, sexp: SEXP) {
186        let send_sexp = sexp.into();
187        let Ownership {
188            preservation,
189            cur_index: _,
190            max_index: _,
191            ref mut objects,
192        } = self;
193
194        let mut entry = objects.entry(send_sexp);
195        match entry {
196            Entry::Occupied(ref mut occupied) => {
197                let object = occupied.get_mut();
198                if object.refcount == 0 {
199                    panic!("Attempt to unprotect an already unprotected object.")
200                } else {
201                    object.refcount -= 1;
202                    if object.refcount == 0 {
203                        // Clear the preservation vector, but keep the hash table entry.
204                        // It is hard to clear the hash table entry here because we don't
205                        // have a ref to objects anymore and it is faster to clear them up en-masse.
206                        let preservation_sexp = preservation.inner();
207                        SET_VECTOR_ELT(preservation_sexp, object.index as R_xlen_t, R_NilValue);
208                    }
209                }
210            }
211            Entry::Vacant(_) => {
212                panic!("Attempt to unprotect a never protected object.")
213            }
214        }
215    }
216
217    #[allow(dead_code)]
218    unsafe fn ref_count(&mut self, sexp: SEXP) -> usize {
219        let Ownership {
220            preservation: _,
221            cur_index: _,
222            max_index: _,
223            ref mut objects,
224        } = *self;
225
226        let mut entry = objects.entry(sexp.into());
227        match entry {
228            Entry::Occupied(ref mut occupied) => occupied.get().refcount,
229            Entry::Vacant(_) => 0,
230        }
231    }
232
233    // Check the consistency of the model.
234    #[allow(dead_code)]
235    unsafe fn check_objects(&mut self) {
236        let preservation_sexp = self.preservation.inner();
237        assert_eq!(self.max_index, LENGTH(preservation_sexp) as usize);
238
239        // println!("\ncheck");
240
241        for (addr, object) in self.objects.iter() {
242            assert!(object.index < self.max_index);
243            let elt = VECTOR_ELT(preservation_sexp, object.index as R_xlen_t);
244            // println!(
245            //     "refcount={:?} index={:?} elt={:?}",
246            //     object.refcount, object.index, elt
247            // );
248            if object.refcount != 0 {
249                // A non-zero refcount implies the object is in the vector.
250                assert_eq!(elt, addr.inner());
251            } else {
252                // A zero refcount implies the object is NULL in the vector.
253                assert_eq!(elt, R_NilValue);
254            }
255        }
256
257        // println!("check 2");
258        for i in 0..self.max_index {
259            let elt = VECTOR_ELT(preservation_sexp, i as R_xlen_t);
260            if elt == R_NilValue {
261                assert_eq!(self.ref_count(elt), 0);
262            } else {
263                assert!(self.ref_count(elt) != 0);
264            }
265        }
266        // println!("/check");
267    }
268}
269
270#[cfg(test)]
271mod test {
272    use super::*;
273    use crate as extendr_api;
274    use crate::*;
275    use extendr_ffi::{Rf_ScalarInteger, Rf_protect};
276
277    #[test]
278    fn basic_test() {
279        test! {
280            single_threaded(|| unsafe {
281                {
282                    let mut own = OWNERSHIP.lock().expect("lock failed");
283                    own.check_objects();
284                }
285
286                let sexp1 = Rf_protect(Rf_ScalarInteger(1));
287                let sexp2 = Rf_protect(Rf_ScalarInteger(2));
288                protect(sexp1);
289                {
290                    let mut own = OWNERSHIP.lock().expect("lock failed");
291                    own.check_objects();
292                    assert_eq!(own.ref_count(sexp1), 1);
293                    assert_eq!(own.ref_count(sexp2), 0);
294                }
295
296                protect(sexp1);
297                {
298                    let mut own = OWNERSHIP.lock().expect("lock failed");
299                    own.check_objects();
300                    assert_eq!(own.ref_count(sexp1), 2);
301                    assert_eq!(own.ref_count(sexp2), 0);
302                }
303
304                unprotect(sexp1);
305                {
306                    let mut own = OWNERSHIP.lock().expect("lock failed");
307                    own.check_objects();
308                    assert_eq!(own.ref_count(sexp1), 1);
309                    assert_eq!(own.ref_count(sexp2), 0);
310                }
311
312                unprotect(sexp1);
313                {
314                    let mut own = OWNERSHIP.lock().expect("lock failed");
315                    own.check_objects();
316                    assert_eq!(own.ref_count(sexp1), 0);
317                    assert_eq!(own.ref_count(sexp2), 0);
318                }
319
320                protect(sexp2);
321                {
322                    let mut own = OWNERSHIP.lock().expect("lock failed");
323                    own.check_objects();
324                    assert_eq!(own.ref_count(sexp1), 0);
325                    assert_eq!(own.ref_count(sexp2), 1);
326                }
327
328                protect(sexp1);
329                {
330                    let mut own = OWNERSHIP.lock().expect("lock failed");
331                    own.check_objects();
332                    assert_eq!(own.ref_count(sexp1), 1);
333                    assert_eq!(own.ref_count(sexp2), 1);
334                }
335                Rf_unprotect(2);
336            });
337        }
338    }
339
340    #[test]
341    fn collection_test() {
342        test! {
343            single_threaded(|| unsafe {
344                {
345                    let mut own = OWNERSHIP.lock().expect("protect failed");
346                    own.check_objects();
347                }
348
349                // Force a garbage collect.
350                let test_size = INITIAL_PRESERVATION_SIZE + EXTRA_PRESERVATION_SIZE * 5;
351
352                // Make some test objects.
353                let sexp_pres = Rf_allocVector(SEXPTYPE::VECSXP, test_size as R_xlen_t);
354                Rf_protect(sexp_pres);
355
356                let sexps = (0..test_size).map(|i| {
357                    let sexp = Rf_ScalarInteger(1);
358                    SET_VECTOR_ELT(sexp_pres, i as R_xlen_t, sexp);
359                    sexp
360                }).collect::<Vec<_>>();
361
362                for (i, sexp) in sexps.iter().enumerate() {
363                    protect(*sexp);
364                    if i % 2 == 0 {
365                        unprotect(*sexp);
366                    }
367                }
368
369                {
370                    let mut own = OWNERSHIP.lock().expect("protect failed");
371                    own.check_objects();
372                    own.garbage_collect();
373                    own.check_objects();
374                }
375
376                Rf_unprotect(1);
377            });
378        }
379    }
380}