diff --git a/Cargo.toml b/Cargo.toml index 6b494faa..f857be65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "rustler_tests/native/rustler_serde_test", "rustler_tests/native/dynamic_load", "rustler_tests/native/rustler_compile_tests", + "rustler_tests/native/resource_dyncall", "rustler_benchmarks/native/benchmark", ] default-members = [ diff --git a/rustler/src/lib.rs b/rustler/src/lib.rs index 005c732e..27144ce8 100644 --- a/rustler/src/lib.rs +++ b/rustler/src/lib.rs @@ -81,3 +81,7 @@ pub mod serde; #[cfg(feature = "serde")] pub use crate::serde::SerdeTerm; + +pub mod sys { + pub use rustler_sys::*; +} diff --git a/rustler_tests/lib/resource_dyncall.ex b/rustler_tests/lib/resource_dyncall.ex new file mode 100644 index 00000000..ec289e38 --- /dev/null +++ b/rustler_tests/lib/resource_dyncall.ex @@ -0,0 +1,11 @@ +if System.otp_release() >= "26" do + defmodule ResourceDyncall do + use Rustler, + otp_app: :rustler_test, + crate: :resource_dyncall + + def new(_), do: err() + + defp err(), do: :erlang.nif_error(:nif_not_loaded) + end +end diff --git a/rustler_tests/lib/rustler_test.ex b/rustler_tests/lib/rustler_test.ex index b09ddb22..cc10377b 100644 --- a/rustler_tests/lib/rustler_test.ex +++ b/rustler_tests/lib/rustler_test.ex @@ -145,4 +145,8 @@ defmodule RustlerTest do def greeting_person_from_tuple(_tuple), do: err() def append_to_path(_path, _to_append), do: err() + + if System.otp_release() >= "26" do + def perform_dyncall(_res, _a, _b, _c), do: err() + end end diff --git a/rustler_tests/native/resource_dyncall/Cargo.toml b/rustler_tests/native/resource_dyncall/Cargo.toml new file mode 100644 index 00000000..94ca9a67 --- /dev/null +++ b/rustler_tests/native/resource_dyncall/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "resource_dyncall" +version = "0.1.0" +edition = "2021" + +[lib] +name = "resource_dyncall" +crate-type = ["cdylib"] + +[dependencies] +rustler = { path = "../../../rustler", features = ["allocator", "nif_version_2_16"] } diff --git a/rustler_tests/native/resource_dyncall/src/lib.rs b/rustler_tests/native/resource_dyncall/src/lib.rs new file mode 100644 index 00000000..a52b8d36 --- /dev/null +++ b/rustler_tests/native/resource_dyncall/src/lib.rs @@ -0,0 +1,32 @@ +use rustler::{Env, LocalPid, Resource, ResourceArc}; + +#[repr(C)] +struct Params { + a: i64, + b: i64, + c: i64, + sent_to: Option, +} + +struct ResourceWithDyncall { + pid: LocalPid, +} + +#[rustler::resource_impl(name = "resource_with_dyncall")] +impl Resource for ResourceWithDyncall { + unsafe fn dyncall<'a>(&'a self, env: Env<'a>, call_data: *mut rustler::sys::c_void) { + let p = &mut *(call_data as *mut Params); + if let Ok(()) = env.send(&self.pid, (p.a, p.b, p.c)) { + p.sent_to = Some(self.pid); + } else { + p.sent_to = None + } + } +} + +#[rustler::nif] +fn new(pid: LocalPid) -> ResourceArc { + ResourceWithDyncall { pid }.into() +} + +rustler::init!("Elixir.ResourceDyncall"); diff --git a/rustler_tests/native/rustler_test/src/lib.rs b/rustler_tests/native/rustler_test/src/lib.rs index 73250eb3..6cb0de85 100644 --- a/rustler_tests/native/rustler_test/src/lib.rs +++ b/rustler_tests/native/rustler_test/src/lib.rs @@ -15,6 +15,8 @@ mod test_resource; mod test_term; mod test_thread; mod test_tuple; +#[cfg(feature = "nif_version_2_16")] +mod test_dyncall; // Intentional usage of the explicit form (in an "invalid" way, listing a wrong set of functions) to ensure that the warning stays alive rustler::init!("Elixir.RustlerTest", [deprecated, usage], load = load); diff --git a/rustler_tests/native/rustler_test/src/test_dyncall.rs b/rustler_tests/native/rustler_test/src/test_dyncall.rs new file mode 100644 index 00000000..30f83998 --- /dev/null +++ b/rustler_tests/native/rustler_test/src/test_dyncall.rs @@ -0,0 +1,37 @@ +use rustler::{Env, LocalPid, Term}; + +rustler::atoms! { + module = "Elixir.ResourceDyncall", + resource_name = "resource_with_dyncall", +} + +#[repr(C)] +struct Params { + a: i64, + b: i64, + c: i64, + sent_to: Option, +} + +#[rustler::nif] +pub fn perform_dyncall<'a>( + env: Env<'a>, + resource: Term<'a>, + a: i64, + b: i64, + c: i64, +) -> Option { + let mut params = Params { + a, + b, + c, + sent_to: None, + }; + + unsafe { + let params_ptr = std::ptr::addr_of_mut!(params) as *mut rustler::sys::c_void; + let _ = env.dynamic_resource_call(module(), resource_name(), resource, params_ptr); + } + + params.sent_to +} diff --git a/rustler_tests/test/resource_dyncall_tests.exs b/rustler_tests/test/resource_dyncall_tests.exs new file mode 100644 index 00000000..19659004 --- /dev/null +++ b/rustler_tests/test/resource_dyncall_tests.exs @@ -0,0 +1,22 @@ +if System.otp_release() >= "26" do + defmodule RustlerTest.ResourceDyncallTest do + use ExUnit.Case, async: true + + test "perform dyncall" do + pid = self() + + res = ResourceDyncall.new(pid) + + call_res = RustlerTest.perform_dyncall(res, 1, 2, 3) + + assert call_res == pid + + receive do + {1, 2, 3} -> true + after + 50 -> + raise "fail" + end + end + end +end