diff --git a/rustler/src/resource/arc.rs b/rustler/src/resource/arc.rs index b935c3fd..a6827cf6 100644 --- a/rustler/src/resource/arc.rs +++ b/rustler/src/resource/arc.rs @@ -172,6 +172,29 @@ impl<'a> Env<'a> { pub fn demonitor(&self, resource: &ResourceArc, mon: &Monitor) -> bool { resource.demonitor(Some(*self), mon) } + + #[cfg(feature = "nif_version_2_16")] + pub unsafe fn dynamic_resource_call( + self, + module: crate::Atom, + name: crate::Atom, + resource: Term<'a>, + call_data: *mut rustler_sys::c_void, + ) -> Result<(), super::DynamicResourceCallError> { + let res = rustler_sys::enif_dynamic_resource_call( + self.as_c_arg(), + module.as_c_arg(), + name.as_c_arg(), + resource.as_c_arg(), + call_data, + ); + + if res == 0 { + Ok(()) + } else { + Err(super::DynamicResourceCallError) + } + } } impl Deref for ResourceArc diff --git a/rustler/src/resource/error.rs b/rustler/src/resource/error.rs index e686aa50..7fe312b5 100644 --- a/rustler/src/resource/error.rs +++ b/rustler/src/resource/error.rs @@ -1,3 +1,8 @@ /// Indicates that a resource has not been registered successfully #[derive(Clone, Copy, Debug)] pub struct ResourceInitError; + +/// Indicates that a dynamic resource call failed +#[allow(dead_code)] +#[derive(Clone, Copy, Debug)] +pub struct DynamicResourceCallError; diff --git a/rustler/src/resource/mod.rs b/rustler/src/resource/mod.rs index 977a38b5..d8023603 100644 --- a/rustler/src/resource/mod.rs +++ b/rustler/src/resource/mod.rs @@ -13,7 +13,7 @@ mod traits; mod util; pub use arc::ResourceArc; -pub use error::ResourceInitError; +pub use error::*; pub use monitor::Monitor; pub use registration::Registration; pub use traits::Resource; diff --git a/rustler/src/resource/registration.rs b/rustler/src/resource/registration.rs index 2c761bcd..ab45bd6f 100644 --- a/rustler/src/resource/registration.rs +++ b/rustler/src/resource/registration.rs @@ -65,6 +65,7 @@ impl Registration { } .maybe_add_destructor_callback::() .maybe_add_down_callback::() + .maybe_add_dyncall_callback::() } pub const fn with_name(self, name: &'static str) -> Self { @@ -104,6 +105,28 @@ impl Registration { } } + #[cfg(not(feature = "nif_version_2_16"))] + #[allow(clippy::extra_unused_type_parameters)] + const fn maybe_add_dyncall_callback(self) -> Self { + self + } + + #[cfg(feature = "nif_version_2_16")] + const fn maybe_add_dyncall_callback(self) -> Self { + if T::IMPLEMENTS_DYNCALL { + Self { + init: ErlNifResourceTypeInit { + dyncall: resource_dyncall:: as *const rustler_sys::ErlNifResourceDynCall, + members: max(self.init.members, 4), + ..self.init + }, + ..self + } + } else { + self + } + } + /// Try to register the resource type for which this registration was created. This function /// will only succeed when called from the `load` callback and if this type has not yet been /// registered. @@ -162,6 +185,19 @@ unsafe extern "C" fn resource_down( res.down(env, pid, mon); } +#[cfg(feature = "nif_version_2_16")] +unsafe extern "C" fn resource_dyncall( + env: *mut ErlNifEnv, + obj: *mut c_void, + call_data: *mut c_void, +) { + let env = Env::new_internal(&env, env, EnvKind::Callback); + let aligned = align_alloced_mem_for_struct::(obj); + let res = &*(aligned as *const T); + + res.dyncall(env, call_data); +} + pub unsafe fn open_resource_type( env: *mut ErlNifEnv, name: &[u8], @@ -175,7 +211,7 @@ pub unsafe fn open_resource_type( let res = { let mut tried = MaybeUninit::uninit(); - rustler_sys::enif_open_resource_type_x(env, name_p, &init, flags, tried.as_mut_ptr()) + OPEN_RESOURCE_TYPE(env, name_p, &init, flags, tried.as_mut_ptr()) }; if res.is_null() { @@ -185,6 +221,20 @@ pub unsafe fn open_resource_type( } } +type OpenResourceTypeFn = unsafe extern "C" fn( + *mut ErlNifEnv, + *const c_char, + *const ErlNifResourceTypeInit, + ErlNifResourceFlags, + *mut ErlNifResourceFlags, +) -> *const ErlNifResourceType; + +#[cfg(feature = "nif_version_2_16")] +static OPEN_RESOURCE_TYPE: OpenResourceTypeFn = rustler_sys::enif_init_resource_type; + +#[cfg(not(feature = "nif_version_2_16"))] +static OPEN_RESOURCE_TYPE: OpenResourceTypeFn = rustler_sys::enif_open_resource_type_x; + const fn max(i: i32, j: i32) -> i32 { if i > j { i diff --git a/rustler/src/resource/traits.rs b/rustler/src/resource/traits.rs index feacdfbf..b1614105 100644 --- a/rustler/src/resource/traits.rs +++ b/rustler/src/resource/traits.rs @@ -35,6 +35,9 @@ pub trait Resource: Sized + Send + Sync + 'static { const IMPLEMENTS_DESTRUCTOR: bool = false; const IMPLEMENTS_DOWN: bool = false; + #[cfg(feature = "nif_version_2_16")] + const IMPLEMENTS_DYNCALL: bool = false; + /// Callback function that is executed right before dropping a resource object. /// /// This callback does not have to be implemented to release associated resources or run @@ -51,6 +54,10 @@ pub trait Resource: Sized + Send + Sync + 'static { /// by `ResourceArc::monitor`. #[allow(unused)] fn down<'a>(&'a self, env: Env<'a>, pid: LocalPid, monitor: Monitor) {} + + #[cfg(feature = "nif_version_2_16")] + #[allow(unused)] + unsafe fn dyncall<'a>(&'a self, env: Env<'a>, call_data: *mut rustler_sys::c_void) {} } #[doc(hidden)]