Send + Sync error

I'm using the below type in my code:

pub struct Account<T> {
    client: Arc<dyn ProgramClient<T>>,
    payer: Arc<dyn Signer>,
    nonce_authority: Option<Arc<dyn Signer>>,
}

This is from a foreign crate so I don't have access to any of the types. I use this type in async functions on which I call await, so I was under the assumption (but I might be totally wrong on this) that this type was Send and Sync.

However, when trying to do the below:

        tokio::spawn(async move {
            Account::new(
                get_program_rpc(Arc::clone(&client)),
                Arc::new(Keypair::from_base58_string(&config.buyer_private_key))
            )
            .get_account_info(&Pubkey::default())
            .await;
        });

I get the error:

future cannot be sent between threads safely
the trait `std::marker::Sync` is not implemented for `(dyn spl_token_client::client::ProgramClient<spl_token_client::client::ProgramRpcClientSendTransaction> + 'static)`
instant_swap.rs(47, 14): future is not `Send` as this value is used across an await
spawn.rs(166, 21): required by a bound in `tokio::spawn`

future cannot be sent between threads safely
the trait `std::marker::Send` is not implemented for `(dyn spl_token_client::client::ProgramClient<spl_token_client::client::ProgramRpcClientSendTransaction> + 'static)`
instant_swap.rs(47, 14): future is not `Send` as this value is used across an await
spawn.rs(166, 21): required by a bound in `tokio::spawn`

future cannot be sent between threads safely
the trait `std::marker::Sync` is not implemented for `(dyn solana_sdk::signature::Signer + 'static)`
instant_swap.rs(47, 14): future is not `Send` as this value is used across an await
spawn.rs(166, 21): required by a bound in `tokio::spawn`

future cannot be sent between threads safely
the trait `std::marker::Send` is not implemented for `(dyn solana_sdk::signature::Signer + 'static)`
instant_swap.rs(47, 14): future is not `Send` as this value is used across an await
spawn.rs(166, 21): required by a bound in `tokio::spawn`

Just for context, get_program_rpc returns a Arc<dyn ProgramClient<ProgramRpcClientSendTransaction>>.

So clearly I'm doing something wrong, also the code

 Account::new(
                get_program_rpc(Arc::clone(&client)),
                Arc::new(Keypair::from_base58_string(&config.buyer_private_key))
            )
            .get_account_info(&Pubkey::default())
            .await;

works fine if I am not using it in the Tokio runtime but a normal async Rust function...
If anyone can help me understand why I'm getting this error (is it just because the type is actually !Send + !Sync?), why it's ok in normal Rust async code and is there anything I can do to make it work?

Thanks!

1 Like

Is get_program_rpc your function? If so, try make it return Arc<dyn ProgramClient<ProgramRpcClientSendTransaction> + Send + Sync>.

I tried that, since indeed get_program_rpc is my function, but I'm then hit with

mismatched types
expected reference `&std::sync::Arc<dyn spl_token_client::client::ProgramClient<_>>`
   found reference `&std::sync::Arc<dyn spl_token_client::client::ProgramClient<spl_token_client::client::ProgramRpcClientSendTransaction> + std::marker::Send + std::marker::Sync>`

when calling the new function for Account

pub fn new(
        client: Arc<dyn ProgramClient<T>>,
        payer: Arc<dyn Signer>,
    ) -> Self {

Where is Account coming from? Do you have a link to the documentation? Maybe get_account_info is only implemented for types T that need to satisfy certain bounds that ProgramRpcClientSendTransaction does not. Or you have a generic parameter T yourself somewhere and are trying to construct an Account<T> instead of an Account<ProgramRpcClientSendTransaction>.

Linking the doc below (and Account is actually Token, my bad):

Do you have any explanation as to why this would work in a main function with the attribute #[tokio::main] but not in a tokio::task?

Like I can literally just move the whole code outside of the tokio::spawn(async move {}) and no error anymore...

Yes, task::spawn requires the spawned future to be Send, whereas Runtime::block_on (what #[tokio::main] desugars to) doesn't.

1 Like

Thanks for the answer, that makes a lot of sense. Do you see any work around I could explore? To give full context, I am actually using this type in combination with coerce-rs. I'm getting this error (which I simplified down to the above example) when implementing Handler<Message> for Actor and calling get_account_info.

From your explanation, this stems from the fact that the Message::Result needs to be 'static + Send + Sync which isn't the case for Token. Not sure if you have any suggestion?

Well, before we get to Message::Result, the future that Hanlder::handle returns must be Send first. Otherwise you get an error once you hit an .await point inside the future.

No idea. Token is decidedly !Send and !Sync. Which actually looks like it might be an oversight of the developers, because if I look at the implementation of Token:

pub struct Token<T> {
    client: Arc<dyn ProgramClient<T>>,
    pubkey: Pubkey, /* token mint */
    decimals: Option<u8>,
    payer: Arc<dyn Signer>,
    program_id: Pubkey,
    nonce_account: Option<Pubkey>,
    nonce_authority: Option<Arc<dyn Signer>>,
    nonce_blockhash: Option<Hash>,
    memo: Arc<RwLock<Option<TokenMemo>>>,
    transfer_hook_accounts: Option<Vec<AccountMeta>>,
}

it looks like they are trying to make it Send and Sync, given they put everything in Arcs and RwLock but didn't add the Send and Sync markers to the trait objects. Maybe raising an issue in the repo could get that fixed for you (if it is indeed an oversight and not desired behaviour).

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.