Skip to main content

native/rx_rust_nif/src/resource.rs

use crate::r_api::Sexp;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;

#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ResourceKind {
    Null,
    Logical,
    Integer,
    Double,
    Character,
    LogicalVector,
    IntegerVector,
    DoubleVector,
    CharacterVector,
    Generic,
    Dataframe,
}

pub struct ResourceState {
    pub sexp: Option<Sexp>,
    #[allow(dead_code)]
    pub kind: ResourceKind,
}

pub struct RxResource {
    pub id: u64,
    pub state: Mutex<ResourceState>,
    pub release_enqueued: AtomicBool,
}

unsafe impl Send for RxResource {}
unsafe impl Sync for RxResource {}

impl RxResource {
    pub fn new(kind: ResourceKind) -> Self {
        Self {
            id: crate::owner::reserve_resource_id(),
            state: Mutex::new(ResourceState { sexp: None, kind }),
            release_enqueued: AtomicBool::new(false),
        }
    }

    pub fn mark_release_enqueued(&self) -> bool {
        self.release_enqueued.swap(true, Ordering::SeqCst)
    }
}

impl rustler::Resource for RxResource {}

impl Drop for RxResource {
    fn drop(&mut self) {
        if self.mark_release_enqueued() {
            return;
        }

        let Ok(mut state) = self.state.lock() else {
            crate::owner::record_release_failure(
                self.id,
                "native R object resource mutex was poisoned during release",
            );
            return;
        };

        if let Some(sexp) = state.sexp.take() {
            crate::owner::submit_release(sexp as usize, self.id);
        } else {
            crate::owner::record_release_skipped(
                self.id,
                "native resource had no R object to release",
            );
        }
    }
}