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
48pub 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+ }
0 commit comments