Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions rustler/src/resource/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ use super::util::align_alloced_mem_for_struct;
use super::ResourceInitError;
use crate::env::EnvKind;
use crate::sys::{
c_char, c_void, ErlNifEnv, ErlNifMonitor, ErlNifPid, ErlNifResourceDown, ErlNifResourceDtor,
ErlNifResourceFlags, ErlNifResourceType, ErlNifResourceTypeInit,
c_char, c_int, c_void, ErlNifEnv, ErlNifEvent, ErlNifMonitor, ErlNifPid, ErlNifResourceDown,
ErlNifResourceDtor, ErlNifResourceFlags, ErlNifResourceStop, ErlNifResourceType,
ErlNifResourceTypeInit,
};
use crate::{Env, LocalPid, Monitor, Resource};
use std::any::TypeId;
Expand Down Expand Up @@ -65,6 +66,7 @@ impl Registration {
type_name: None,
}
.maybe_add_destructor_callback::<T>()
.maybe_add_stop_callback::<T>()
.maybe_add_down_callback::<T>()
.maybe_add_dyncall_callback::<T>()
}
Expand Down Expand Up @@ -92,6 +94,22 @@ impl Registration {
}
}

const fn maybe_add_stop_callback<T: Resource>(self) -> Self {
if T::IMPLEMENTS_STOP {
Self {
init: ErlNifResourceTypeInit {
stop: resource_stop::<T> as *const ErlNifResourceStop,
#[cfg(feature = "nif_version_2_16")]
members: max(self.init.members, 2),
..self.init
},
..self
}
} else {
self
}
}

const fn maybe_add_down_callback<T: Resource>(self) -> Self {
if T::IMPLEMENTS_DOWN {
Self {
Expand Down Expand Up @@ -173,6 +191,23 @@ where
}
}

unsafe extern "C" fn resource_stop<T>(
caller_env: *mut ErlNifEnv,
handle: *mut c_void,
event: ErlNifEvent,
is_direct_call: c_int,
) where
T: Resource,
{
let env = Env::new_internal(&caller_env, caller_env, EnvKind::Callback);
let aligned = align_alloced_mem_for_struct::<T>(handle);
let obj = ptr::read::<T>(aligned as *mut T);
let is_direct_call = is_direct_call == 1;
if T::IMPLEMENTS_STOP {
obj.stop(env, event, is_direct_call);
}
}

unsafe extern "C" fn resource_down<T: Resource>(
env: *mut ErlNifEnv,
obj: *mut c_void,
Expand Down
19 changes: 14 additions & 5 deletions rustler/src/resource/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::any::TypeId;
use std::collections::HashMap;
use std::sync::OnceLock;

use crate::sys::ErlNifResourceType;
use crate::sys::{ErlNifEvent, ErlNifResourceType};
use crate::{Env, LocalPid, Monitor};

type NifResourcePtr = *const ErlNifResourceType;
Expand Down Expand Up @@ -30,14 +30,15 @@ pub(crate) unsafe fn register_resource_type(type_id: TypeId, resource_type: NifR
/// In particular, the type needs to handle all synchronization itself (thus we require it to
/// implement `Sync`) and callbacks or NIFs can run on arbitrary threads (thus we require `Send`).
///
/// Currently only `destructor` and `down` callbacks are possible. If a callback is implemented,
/// the respective associated constant `IMPLEMENTS_...` must be set to `true` for the registration
/// to take it into account. All callbacks provide (empty) default implementations.
/// If a callback is implemented, the respective associated constant `IMPLEMENTS_...`
/// must be set to `true` for the registration to take it into account.
/// All callbacks provide (empty) default implementations.
pub trait Resource: Sized + Send + Sync + 'static {
const IMPLEMENTS_DESTRUCTOR: bool = false;
const IMPLEMENTS_STOP: bool = false;
const IMPLEMENTS_DOWN: bool = false;

#[cfg(feature = "nif_version_2_16")]
#[cfg(feature = "nif_version_2_15")]
const IMPLEMENTS_DYNCALL: bool = false;

/// Callback function that is executed right before dropping a resource object.
Expand All @@ -49,6 +50,14 @@ pub trait Resource: Sized + Send + Sync + 'static {
#[allow(unused_mut, unused)]
fn destructor(mut self, env: Env<'_>) {}

/// The stop callback of a resource.
/// It is called on the behalf of [`enif_select()`](crate::sys::enif_select).
///
/// - event is the OS event
/// - is_direct_call is true if the call is made directly from enif_select or false if it is a scheduled call (potentially from another thread).
#[allow(unused)]
fn stop<'a>(&'a self, env: Env<'a>, event: ErlNifEvent, is_direct_call: bool) {}

/// Callback function to handle process monitoring.
///
/// This callback is called when a process monitored using `Env::monitor` terminates
Expand Down
Loading