Idea:
An .await
invocation on a #[inline(always)]
async fn
should make that .await
behave as a no-op
and execute given the async function in the context of the current future, without yielding control back to the executor.
Usecase:
Performance. Sometimes you don't want to yield control, for data-race / latency / performance reasons, but you still want to be able to partition your async
functions into more managable chunks. This would allow that.
Current workaround:
A macro "inline function generator " (code block with parameter bindings). This way, I can create a "fake" function which uses .await
and be able to call it from within an async
context without an executor yield.
https://gist.github.com/vjancik/0ae810
#![allow(unused_macros)]
macro_rules! with_dollar_sign {
($($body:tt)*) => {
macro_rules! __with_escape { $($body)* }
__with_escape!($);
}
}
macro_rules! inline_fn {
( $func:ident, $( $par_name:ident $(: $par_type:ty )?,)* $func_body:block) => {
paste::paste! {
with_dollar_sign! { ($d:tt) => {
macro_rules! $func {
( $( $par_name = $d [< $par_name _par_val >]:expr ),* ) => {{
$(
let $par_name $(: $par_type)? = $d [< $par_name _par_val >];
)*
$func_body
}}
}
}}
}
};
}
#[cfg(test)]
mod tests {
use tokio::runtime;
inline_fn!(some_func, some_var: &mut u32, some_var2, {
*some_var -= 1;
assert_eq!(*some_var, 4);
assert_eq!(some_var2, 6);
});
#[test]
fn inline_test() {
let mut captured = 5;
some_func!(some_var = &mut captured, some_var2 = 6);
}
inline_fn!(inline_async, {
(async {
println!("Executed asynchronously");
return 0u8
}).await
});
async fn async_routine() -> u8 {
inline_async!()
}
#[test]
fn inline_async_test() {
let runtime = runtime::Builder::new_current_thread().build().unwrap();
runtime.block_on(async_routine());
}
}
Note: That is the first "real" macro I've ever written in Rust, so any improvement suggestions are very welcome!