-
Notifications
You must be signed in to change notification settings - Fork 5
refactor: use gstd::message_loop
only for async ctors and service methods
#939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
By the b3eb14 it was agreed to implement sync |
try_handle
if service has no async methodsgstd::message_loop
only for async ctors and service methods
syn = { workspace = true, features = ["full", "extra-traits"] } | ||
tokio = { workspace = true, features = ["full"] } | ||
trybuild = "1.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move to workspace deps, pls
@@ -84,6 +84,16 @@ pub fn unknown_input_panic(message: &str, input: &[u8]) -> ! { | |||
pub trait InvocationIo { | |||
const ROUTE: &'static [u8]; | |||
type Params: Decode; | |||
const ASYNC: bool; | |||
|
|||
fn check_route(payload: impl AsRef<[u8]>) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better is_acync
or check_asyncness
returns Result<bool>
fn is_async(payload: impl AsRef<[u8]>) -> Result<bool> {
let value = payload.as_ref();
if !value.starts_with(Self::ROUTE) {
return Err(Error::Rtl(RtlError::InvocationPrefixMismatches));
}
Ok(Self::ASYNC)
}
quote! { | ||
<#path_wo_lifetimes as #sails_path::meta::ServiceMeta>::ASYNC | ||
} | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we do not need to generate ASYNC in impl CommandsMeta, QueriesMeta
here we know exactly if we have any async fn
so, if yes - just set true
else - check base services
let is_any_async = self
.service_handlers
.iter()
.any(|fn_builder| fn_builder.is_async());
let service_meta_asyncness = if is_any_async {
quote!(true)
} else {
if self.base_types.is_empty() {
quote!(false)
} else {
let base_asyncness = self.base_types.iter().map(|base_type| {
let path_wo_lifetimes = shared::remove_lifetimes(base_type);
quote! {
<#path_wo_lifetimes as #sails_path::meta::ServiceMeta>::ASYNC
}
});
quote!(#( #base_asyncness )||*)
}
};
const ASYNC: bool = #service_meta_asyncness;
@@ -49,10 +62,14 @@ impl ServiceBuilder<'_> { | |||
(fn_builder.is_query()).then_some(fn_builder.handler_meta_variant()) | |||
}); | |||
|
|||
let commands_asyncness = self.service_handler_asyncness(false); | |||
let queries_asyncness = self.service_handler_asyncness(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not needed
@@ -62,12 +79,20 @@ impl ServiceBuilder<'_> { | |||
#(#commands_meta_variants),* | |||
} | |||
|
|||
impl CommandsMeta { | |||
pub const ASYNC: bool = #commands_asyncness; | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not needed
@@ -76,4 +101,23 @@ impl ServiceBuilder<'_> { | |||
} | |||
} | |||
} | |||
|
|||
fn service_handler_asyncness(&self, is_query: bool) -> TokenStream { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not needed
|
||
impl ConstructorsMeta { | ||
pub const ASYNC: bool = #( #ctors_asyncness )||*; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not needed
let mut invocation_dispatches = Vec::new(); | ||
let mut routes = BTreeMap::new(); | ||
// only used for ethexe | ||
#[allow(unused_mut)] | ||
let mut solidity_dispatchers: Vec<TokenStream2> = Vec::new(); | ||
|
||
meta_asyncness.push(quote!(meta_in_program::ConstructorsMeta::ASYNC)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here we know exactly if we have any async ctor
so, if yes - just set true
else - set from services
}); | ||
|
||
quote! { | ||
pub fn check_asyncness(&self, #input_ident : &[u8]) -> Option<bool> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To think about, maybe first check cosnt
<Service as ServiceMeta>::ASYNC
for the compiler to optimize the condition.
gstd::unknown_input_panic("Unknown request", input) | ||
let Some(is_async) = service.check_asyncness(&input[#route_ident .len()..]) else { | ||
gstd::unknown_input_panic("Unknown call", &input[#route_ident .len()..]) | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let is_async = service.check_asyncness(&input[#route_ident .len()..]).unwrap_or_else(|| gstd::unknown_input_panic("Unknown call", &input[#route_ident .len()..]));
Resolves #63
The PR suggests not using async runtime from the
gstd
by default for any message sent to program. The separation between calls that require async runtime and sync execution was benched (see comments below) and it was concluded that the separation saves 100-200kk of gas. The changes in code generation are the following:InvocationIo
trait now hasASYNC
constant.service
andprogram
macros define theASYNC
value for each constructor and service method that implementsInvocationIo
.check_asyncness
method that accepts encoded service method route and params and returns whether the method is async. Basically, returnsASYNC
const of theInvocationIo
implementor.service
macro now generates 2try_handle
methods:try_handle
andtry_handle_async
. The former invokes sync method, and the latter - async ones. Methods also calltry_handle*
methods of base services.Commands
andQueries
hasASYNC
const in their impl items. By default, theASYNC
isfalse
. If there are any commands and queries, theirInvocationIo::ASYNC
values will be used.ServiceMeta
trait now also hasASYNC
constant. The value is generated fromCommands::ASYNC
,Queries::ASYNC
andASYNC
values of base services, which also implement theServiceMeta
trait.ConstructorsMeta
now also hasASYNC
constant in its impl item. The value of the constant is defined byInvocationIo::ASYNC
of constructors (params structs).program
macro never usesgstd::async_init
orgstd::async_main
. Instead generates plaininit
,handle
,handle_reply
andhandle_signal
.init
method invokes constructors. If a constructor is an async, thengstd::message_loop
will be used. Otherwise a simple sync constructor call will be generated.ProgramMeta
trait now also hasASYNC
constant. It's value is defined fromConstructorsMeta::ASYNC
andServiceMeta::ASYNC
of programs services. All in all it's a logical operation where each operand is anASYNC
value for each constructor and service method (params struct)handle_signal
is always generated except for cases whenethexe
feature is onhandle_reply
is always generated. Both inhandle_reply
andhandle_signal
hooks fromgstd
are called only ifProgramMeta::ASYNC
value istrue
. The latter means that there's somewhere in programgstd::message_loop
is used for async methods execution. So due to the fact that async methods are fromgstd
and thewait
/wake
logic is already handled by hooks ingstd
, these hooks must be used inhandle_reply
andhandle_signal
to implement proper async/await experience for developers.ethexe
feature there are several corresponding changes:try_handle_solidity
andtry_handle_solidity_async
are also implemented pretty same astry_handle*
match_ctor_solidity
is now a sync function that in case the constructor is async, invokes it insidegstd::message_loop
.TODO:
̶e̶x̶a̶m̶p̶l̶e̶s̶
̶ ̶(̶a̶n̶d̶ ̶̶e̶t̶h̶e̶x̶e̶
̶)̶ (UPD: done)