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
WIP: ?Send support #1122
base: master
Are you sure you want to change the base?
WIP: ?Send support #1122
Conversation
I'm looking forward to this PR, come on bro! 👏 |
@sunli829 Thanks for being to this feature. I'll be working on it for the next couple weeks! |
Very interesting! May I ask in which case do you have a single-threaded environment for a GraphQL API which is, by nature, more tailored to be split across multiple threads? |
@ClementNerma I for one have a use-case in a WASM based runtime, like running async-graphql in a Cloudflare Worker, which is single threaded. |
7eabbe3
to
912feb3
Compare
@omarabid whats left to ship this? |
https://crates.io/crates/send_wrapper You can use this, there's a slight performance penalty. |
The PR is working (though the latest from master is not merged) and I am using it. I'm caught travelling but hopefully will get to it this week. Here are the steps if you are looking to help:
|
Hi @sunli829, Please advice on the following implementation /// Used to specify the GraphQL Type name.
#[cfg(feature = "no-send")]
pub trait TypeName {
/// Returns a GraphQL type name.
fn type_name() -> Cow<'static, str>;
}
#[cfg(not(feature = "no-send"))]
pub trait TypeName: Send + Sync {
/// Returns a GraphQL type name.
fn type_name() -> Cow<'static, str>;
} Alternatively, you can have a default |
I think this can be better, what do you think? #[cfg(feature = "no-send")]
pub trait ThreadedModel {
// wrap the future in a box
fn boxed_future<F: Future>(fut: F) {
Box::new(fut)
}
}
#[cfg(not(feature = "no-send"))]
pub trait ThreadedModel: Send + Sync {
fn boxed_future<F: Future + Send + Sync>(fut: F) {
Box::new(fut)
}
}
pub trait TypeName: ThreadedModel {
/// Returns a GraphQL type name.
fn type_name() -> Cow<'static, str>;
} |
@sunli829 This has the interesting effect of requiring that any type that implements Also, is this needed? fn boxed_future<F: Future + Send + Sync>(fut: F) {
Box::new(fut)
} |
I think we can use macros to implement A different BoxFuture definition needs to be used here. #[cfg(feature = "no-send")]
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T>> + 'a>>
#[cfg(not(feature = "no-send"))]
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T>> + 'a + Send>>
type BoxFieldFuture<'a> = BoxFuture<'a, ServerResult<(Name, Value)>>; async-graphql/src/resolver_utils/container.rs Line 174 in 1befbb1
|
Latest from upstream merged. @sunli829 Is this feature still of interest? I am looking to give it another shot and have a bit of free time in the next couple weeks. Your guidance in that will be very helpful. |
Yes, I hope to have it done before the 6.0 release. |
@sunli829 proc_macro implementation #[proc_macro_attribute]
pub fn threaded_model(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut trait_item = parse_macro_input!(item as ItemTrait);
if !cfg!(feature = "no-send") {
let send_bound: TypeParamBound = syn::parse_quote!(Send);
let sync_bound: TypeParamBound = syn::parse_quote!(Sync);
trait_item.supertraits.push(syn::parse_quote!(#send_bound));
trait_item.supertraits.push(syn::parse_quote!(#sync_bound));
}
TokenStream::from(quote! { #trait_item })
} Usage #[threaded_model]
pub trait TypeName {
/// Returns a GraphQL type name.
fn type_name() -> Cow<'static, str>;
} There is one catch, however. The no-send feature doesn't seem like it transcends to the derive crate. Any ideas how to do that? |
Maybe the following way will be easier, what do you think? #[cfg(not(feature="no-send"))]
trait ThreadedModel: Send + Sync {}
#[cfg(feature="no-send")]
trait ThreadedModel {}
pub trait TypeName: ThreadedModel {
/// Returns a GraphQL type name.
fn type_name() -> Cow<'static, str>;
} |
@sunli829 That's what I've been trying to do (and mostly because I am new to macros). The problem is that the Send and Sync traits are marker traits; and they behave differently from a normal trait. As a result, you don't have implied implementation and you have to implement ThreadedModel for every type that implements TypeName. I've pushed that attempt here: https://github.com/omarabid/async-graphql/tree/trait if you have time to debug it. |
Looks great, do you need my help fixing the procedural macros? @omarabid |
@omarabid I'm still new to macros/proc_macros. I'll give it a couple days and come back with an implementation for you to review. |
@omarabid would you like some help? |
@pandranki-skytv It has been a while since I tried my hands on this, so the details are kind of fuzzy in my brain. However, my latest attempt landed me here: https://users.rust-lang.org/t/optional-send-sync-implementations-through-a-feature-flag/96007 but I never got to H2CO3 proposition. I think it'll be a good idea to start from trying to use a simple Trait in order to understand the different problematic edge cases and then try H2CO3 proposition. I'm a bit swamped now but could get back to it in the next couple weeks. |
404c7b2
to
0142d6a
Compare
This PR removes support for
Send
andSync
. The use case is for people running async-graphql on a single threaded environment and who are limited by having types that are notSend
andSync
. The current solution to wrap these types is very limited and messy.This PR is intrusive; however, if you are open to accepting this as a feature (ie: no-send), I'd work on making the necessary changes. We are adopting
async-graphql
as our main graphql server; so I'll be updating from upstream and merging conflicts accordingly for the time being.