savvy/sexp/external_pointer.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
use savvy_ffi::{
R_ClearExternalPtr, R_ExternalPtrAddr, R_MakeExternalPtr, R_NilValue, R_RegisterCFinalizerEx,
SEXP,
};
use crate::{protect::local_protect, Sexp};
// Some notes about the design.
//
// 1. conversion from Rust struct into SEXP
//
// The result `EXTPTRSXP` is returned as unprotected because the conversion
// happens very before returning the result to C's side. This means there should
// be no more call to R API until it's finally passed to the R session.
//
// 2. conversion from SEXP into Rust struct
//
// This conversion is handled in macro because it's basically just
// `R_ExternalPtrAddr(x) as *mut T` if we can optimistically assume the user never
// supply a wrong input. This assumption should be ensured on R's side.
pub trait IntoExtPtrSexp: Sized {
// Note: I can add two more arguments here just as cpp11 does
// (https://github.com/r-lib/cpp11/blob/500f642b4ea132ec8c168fc70a28e81e9510ece3/inst/include/cpp11/external_pointer.hpp#L58)
//
// 1. use_deleter
//
// It's not always the cleanup process should be called automatically. If
// this is false, R_RegisterCFinalizerEx() is not called.
//
// 2. finalize_on_exit
//
// R_RegisterCFinalizerEx() has `onexit` argument. On R-exts, it is
// described as below:
//
// > the onexit argument of the extended forms can be used to ask that the
// > finalizer be run during a normal shutdown of the R session.
//
// I'm not immediately sure about the pros and cons, but I bet it's good to
// enable this by default.
fn into_external_pointer(self) -> Sexp {
let boxed = Box::new(self);
let ptr = Box::into_raw(boxed);
unsafe extern "C" fn finalizer<T>(x: SEXP) {
// bring back the ownership to Rust's side so that Rust will drop
// after this block ends.
let ptr = unsafe { R_ExternalPtrAddr(x) };
// the pointer can be null (e.g. https://github.com/pola-rs/r-polars/issues/851)
if !ptr.is_null() {
let rust_obj = unsafe { Box::from_raw(ptr as *mut T) };
drop(rust_obj);
}
unsafe { R_ClearExternalPtr(x) };
}
unsafe {
let external_pointer =
R_MakeExternalPtr(ptr as *mut std::os::raw::c_void, R_NilValue, R_NilValue);
let _external_pointer_guard = local_protect(external_pointer);
// Use R_RegisterCFinalizerEx(..., TRUE) instead of
// R_RegisterCFinalizer() in order to make the cleanup happen during
// a shutdown of the R session as well.
R_RegisterCFinalizerEx(external_pointer, Some(finalizer::<Self>), 1);
Sexp(external_pointer)
}
}
}
/// A Wrapper of R_ExternalPtrAddr() to use in savvy-bindgen
///
/// # Safety
/// This is intended to be used only in savvy-bindgen
pub unsafe fn get_external_pointer_addr(
x: SEXP,
) -> crate::error::Result<*mut std::os::raw::c_void> {
let ptr = unsafe { R_ExternalPtrAddr(x) };
if ptr.is_null() {
return Err(crate::error::Error::InvalidPointer);
}
Ok(ptr)
}
/// Takes the value of the external pointer and set the pointer to null.
///
/// # Safety
/// This is intended to be used only in savvy-bindgen
pub unsafe fn take_external_pointer_value<T>(x: SEXP) -> crate::error::Result<T> {
let ptr = unsafe { R_ExternalPtrAddr(x) };
if !ptr.is_null() {
let rust_obj = unsafe { Box::from_raw(ptr as *mut T) };
// Set the pointer to null
unsafe { R_ClearExternalPtr(x) };
Ok(*rust_obj)
} else {
Err(crate::error::Error::InvalidPointer)
}
}
/// An **external** external pointer.
///
/// This exists solely for casting a EXTPTRSXP into the underlying type.
pub struct ExternalPointerSexp(pub SEXP);
impl ExternalPointerSexp {
pub fn inner(&self) -> SEXP {
self.0
}
/// Cast the SEXP to a concrete type of pointer.
///
/// # Safety
///
/// This function is highly unsafe in that there's no mechanism to verify
/// the destination type is the correct one.
pub unsafe fn cast_unchecked<T>(&self) -> *const T {
unsafe { savvy_ffi::R_ExternalPtrAddr(self.0) as _ }
}
/// Cast the SEXP to a concrete type of pointer.
///
/// # Safety
///
/// This function is highly unsafe in that there's no mechanism to verify
/// the destination type is the correct one.
pub unsafe fn cast_mut_unchecked<T>(&self) -> *mut T {
unsafe { savvy_ffi::R_ExternalPtrAddr(self.0) as _ }
}
}
impl TryFrom<Sexp> for ExternalPointerSexp {
type Error = crate::error::Error;
fn try_from(value: Sexp) -> crate::error::Result<Self> {
// Return error if the SEXP is not an external pointer
value.assert_external_pointer()?;
Ok(Self(value.0))
}
}