Skip to content

Commit 8ca2170

Browse files
committed
cargo fmt, adds nif scheduling type and macro
1 parent 6330936 commit 8ca2170

File tree

11 files changed

+164
-25
lines changed

11 files changed

+164
-25
lines changed

rustler/Cargo.toml

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,6 @@ authors = ["Hansihe <me@hansihe.com>"]
77
license = "MIT/Apache-2.0"
88
edition = "2018"
99

10-
[features]
11-
default = ["derive"]
12-
derive = ["rustler_codegen"]
13-
alternative_nif_init_name = []
14-
15-
[dependencies]
16-
rustler_codegen = { path = "../rustler_codegen", optional = true }
17-
erl_nif_sys = { path = "../erl_nif_sys" }
18-
lazy_static = "1.2"
19-
20-
[build-dependencies]
21-
lazy_static = "1.2"
22-
which = "2"
23-
2410
[package.metadata.release]
2511

2612
[[package.metadata.release.pre-release-replacements]]
@@ -37,3 +23,17 @@ replace = "def rustler_version, do: \"{{version}}\""
3723
file = "../rustler_codegen/Cargo.toml"
3824
search = "# rustler_codegen version\nversion = \"[^\"]+\""
3925
replace = "# rustler_codegen version\nversion = \"{{version}}\""
26+
27+
[features]
28+
default = ["derive"]
29+
derive = ["rustler_codegen"]
30+
alternative_nif_init_name = []
31+
32+
[dependencies]
33+
erl_nif_sys = { path = "../erl_nif_sys" }
34+
lazy_static = "1.2"
35+
rustler_codegen = { path = "../rustler_codegen", optional = true }
36+
37+
[build-dependencies]
38+
lazy_static = "1.2"
39+
which = "2"

rustler/src/codegen_runtime.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ unsafe impl<T> NifReturnable for T
2121
where
2222
T: crate::Encoder,
2323
{
24+
#[inline]
2425
unsafe fn as_returned(self, env: Env) -> NifReturned {
2526
NifReturned::Term(self.encode(env).as_c_arg())
2627
}
@@ -31,7 +32,7 @@ where
3132
{
3233
unsafe fn as_returned(self, env: Env) -> NifReturned {
3334
match self {
34-
Ok(inner) => NifReturned::Term(inner.encode(env).as_c_arg()),
35+
Ok(inner) => inner.as_returned(env),
3536
Err(inner) => inner.as_returned(env),
3637
}
3738
}

rustler/src/export.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,31 @@ macro_rules! rustler_export_nifs {
2121
$crate::rustler_export_nifs!($name, [$( $exported_nif ),*], $on_load);
2222
};
2323
($name:expr, [$( $exported_nif:tt ),*], $on_load:expr) => {
24+
type ExternNifFuncMap = std::collections::HashMap<&'static str, ExternNifFunc>;
25+
type ExternNifFunc = unsafe extern "C" fn(env: $crate::codegen_runtime::NIF_ENV,
26+
argc: $crate::codegen_runtime::c_int,
27+
argv: *const $crate::codegen_runtime::NIF_TERM)
28+
-> $crate::codegen_runtime::NIF_TERM;
29+
2430
static mut NIF_ENTRY: Option<$crate::codegen_runtime::DEF_NIF_ENTRY> = None;
2531

32+
const FUN_ENTRIES: &'static [$crate::codegen_runtime::DEF_NIF_FUNC] = &[
33+
$($crate::rustler_export_nifs!(internal_item_init, $exported_nif)),*
34+
];
35+
36+
lazy_static::lazy_static!{
37+
pub static ref EXTERN_NIF_MAP: ExternNifFuncMap = {
38+
let mut map: ExternNifFuncMap = std::collections::HashMap::new();
39+
for nif_func_entry in FUN_ENTRIES {
40+
let name = unsafe {
41+
std::ffi::CStr::from_ptr(nif_func_entry.name as *const i8).to_str().unwrap()
42+
};
43+
map.insert(name, nif_func_entry.function);
44+
}
45+
map
46+
};
47+
}
48+
2649
$crate::rustler_export_nifs!(internal_platform_init, ({
2750
// TODO: If an unwrap ever happens, we will unwind right into C! Fix this!
2851

@@ -36,10 +59,6 @@ macro_rules! rustler_export_nifs {
3659
}
3760
}
3861

39-
const FUN_ENTRIES: &'static [$crate::codegen_runtime::DEF_NIF_FUNC] = &[
40-
$($crate::rustler_export_nifs!(internal_item_init, $exported_nif)),*
41-
];
42-
4362
let entry = $crate::codegen_runtime::DEF_NIF_ENTRY {
4463
major: $crate::codegen_runtime::NIF_MAJOR_VERSION,
4564
minor: $crate::codegen_runtime::NIF_MINOR_VERSION,

rustler/src/schedule.rs

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
use crate::wrapper::ErlNifTaskFlags;
2-
use crate::Env;
1+
use crate::{
2+
codegen_runtime::{NifReturnable, NifReturned},
3+
wrapper::{ErlNifTaskFlags, NIF_ENV, NIF_TERM},
4+
Env,
5+
};
6+
use std::ffi::CString;
37

48
pub enum SchedulerFlags {
59
Normal = ErlNifTaskFlags::ERL_NIF_NORMAL_JOB as isize,
@@ -11,3 +15,89 @@ pub fn consume_timeslice(env: Env, percent: i32) -> bool {
1115
let success = unsafe { erl_nif_sys::enif_consume_timeslice(env.as_c_arg(), percent) };
1216
success == 1
1317
}
18+
19+
/// An `Either`-like type representing either the final return value of a NIF, or the parameters required to schedule a future NIF call.
20+
///
21+
/// See `schedule_nif!` for a convenience wrapper around this type.
22+
pub enum NifScheduleResult<T> {
23+
Final(T),
24+
Continue {
25+
fun_name: &'static str,
26+
fun: unsafe extern "C" fn(NIF_ENV, i32, *const NIF_TERM) -> NIF_TERM,
27+
args: Vec<T>,
28+
flags: SchedulerFlags,
29+
},
30+
}
31+
32+
impl<T> From<T> for NifScheduleResult<T>
33+
where
34+
T: crate::Encoder,
35+
{
36+
#[inline]
37+
fn from(t: T) -> Self {
38+
NifScheduleResult::Final(t)
39+
}
40+
}
41+
42+
unsafe impl<T> NifReturnable for Result<NifScheduleResult<T>, crate::error::Error>
43+
where
44+
T: crate::Encoder,
45+
{
46+
unsafe fn as_returned(self, env: Env) -> NifReturned {
47+
match self {
48+
Err(inner) => inner.as_returned(env),
49+
Ok(inner) => match inner {
50+
NifScheduleResult::Final(inner) => inner.as_returned(env),
51+
NifScheduleResult::Continue {
52+
fun_name,
53+
fun,
54+
flags,
55+
args,
56+
} => NifReturned::Reschedule {
57+
fun_name: CString::new(fun_name).unwrap(),
58+
fun,
59+
flags,
60+
args: args.iter().map(|arg| arg.encode(env).as_c_arg()).collect(),
61+
},
62+
},
63+
}
64+
}
65+
}
66+
67+
/// Convenience macro for scheduling a NIF call.
68+
///
69+
/// NOTE: can only be used when the `static ref EXTERN_NIF_MAP` created by `rustler_export_nifs!` macro is in scope.
70+
///
71+
/// ## Example:
72+
/// ```
73+
/// rustler_export_nifs!(
74+
/// "Elixir.RustlerTest",
75+
/// ("factorial", 2, test_schedule::factorial),
76+
/// None,
77+
/// )
78+
///
79+
/// pub fn factorial<'a>(_env: Env<'a>, args: &[Term<'a>]) -> NifResult<NifScheduleResult<u32>> {
80+
/// let input: u32 = args[0].decode()?;
81+
/// let result: u32 = args[1].decode::<Option<u32>>()?.unwrap_or(1);
82+
/// if input == 0 {
83+
/// return Ok(result.into());
84+
/// }
85+
86+
/// return schedule_nif!("factorial", vec![input - 1, result * input]);
87+
/// }
88+
/// ```
89+
#[macro_export]
90+
macro_rules! schedule_nif {
91+
($fun_name:expr, $args:expr) => {
92+
schedule_nif!($fun_name, $args, $crate::schedule::SchedulerFlags::Normal)
93+
};
94+
($fun_name:expr, $args:expr, $flags:expr) => {{
95+
let func = EXTERN_NIF_MAP.get($fun_name).unwrap();
96+
Ok($crate::schedule::NifScheduleResult::Continue {
97+
fun_name: $fun_name,
98+
fun: *func,
99+
args: $args,
100+
flags: $flags,
101+
})
102+
}};
103+
}

rustler/src/term.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::types::binary::OwnedBinary;
22
use crate::wrapper::env::term_to_binary;
33
use crate::wrapper::NIF_TERM;
4-
use crate::{Decoder, Env, NifResult, Binary};
4+
use crate::{Binary, Decoder, Env, NifResult};
55
use std::cmp::Ordering;
66
use std::fmt::{self, Debug};
77

rustler/src/thread.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::env::OwnedEnv;
2-
use std::panic;
3-
use std::thread;
42
use crate::types::atom::Atom;
53
use crate::{Encoder, Env, Term};
4+
use std::panic;
5+
use std::thread;
66

77
/// A `JobSpawner` is a value that can run Rust code on non-Erlang system threads.
88
/// Abstracts away details of thread management for `spawn()`.

rustler_codegen/src/ex_struct.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,11 @@ pub fn gen_decoder(
8383
let ident = field.ident.as_ref().unwrap();
8484
let ident_str = ident.to_string();
8585
let atom_fun = Ident::new(&format!("atom_{}", ident_str), Span::call_site());
86-
let error_message = format!("Could not decode field :{} on %{}{{}}", ident.to_string(), struct_name.to_string());
86+
let error_message = format!(
87+
"Could not decode field :{} on %{}{{}}",
88+
ident.to_string(),
89+
struct_name.to_string()
90+
);
8791
quote! {
8892
#ident: match ::rustler::Decoder::decode(term.map_get(#atom_fun().encode(env))?) {
8993
Err(_) => return Err(::rustler::Error::RaiseTerm(Box::new(#error_message))),

rustler_tests/lib/rustler_test.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,6 @@ defmodule RustlerTest do
6363
def dirty_cpu(), do: err()
6464

6565
def sum_range(_), do: err()
66+
67+
def scheduled_fac(_, _ \\ nil), do: err()
6668
end

rustler_tests/native/rustler_test/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod test_map;
1111
mod test_primitives;
1212
mod test_range;
1313
mod test_resource;
14+
mod test_schedule;
1415
mod test_term;
1516
mod test_thread;
1617

@@ -89,6 +90,7 @@ rustler::rustler_export_nifs!(
8990
),
9091
("dirty_io", 0, test_dirty::dirty_io, SchedulerFlags::DirtyIo),
9192
("sum_range", 1, test_range::sum_range),
93+
("scheduled_fac", 2, test_schedule::scheduled_fac),
9294
],
9395
Some(on_load)
9496
);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use super::EXTERN_NIF_MAP;
2+
use rustler::schedule::NifScheduleResult;
3+
use rustler::schedule_nif;
4+
use rustler::{Env, NifResult, Term};
5+
6+
pub fn scheduled_fac<'a>(_env: Env<'a>, args: &[Term<'a>]) -> NifResult<NifScheduleResult<u32>> {
7+
let input: u32 = args[0].decode()?;
8+
let result: u32 = args[1].decode::<Option<u32>>()?.unwrap_or(1);
9+
if input == 0 {
10+
return Ok(result.into());
11+
}
12+
13+
return schedule_nif!("scheduled_fac", vec![input - 1, result * input]);
14+
}

0 commit comments

Comments
 (0)