I am struggling to wrap my head around Send
and the async
context. At present, I am interacting with solana-sdk which has a Signature
object which does not implement Send
. I interact with this library in an async context in new tasks spawned in tokio.
To illustrate the problem outside of the context of the SDK, I have the following minimum reproducible code:
#![feature(negative_impls)]
use std::sync::Arc;
use tokio::sync::Mutex;
// Library Code
//-------------------------------------------------------------------------------------------------
struct GoofyProperty {}
impl !Send for GoofyProperty {}
impl !Sync for GoofyProperty {}
struct GoofyBuilder { pub prop: GoofyProperty }
// This guy is also !Send and !Sync, implicitly
impl GoofyBuilder {
pub fn new() -> Self { Self { prop: GoofyProperty {} } }
pub fn x(&mut self) -> &mut Self { /* Do some "building". */ self }
pub fn y(&mut self) -> &mut Self { /* Do some "building". */ self }
pub async fn do_some_request(&self) { /* Perform some API call */ }
}
struct GoofyProgram { }
// Implicitly...
// impl Send for GoofyProgram {};
// impl Sync for GoofyProgram {};
impl GoofyProgram {
pub fn builder(&self) -> GoofyBuilder { GoofyBuilder::new() }
}
// My Code
//-------------------------------------------------------------------------------------------------
struct SomeStatefulManager {}
impl SomeStatefulManager {
pub async fn service(&mut self) {
let my_program = GoofyProgram{};
my_program.builder()
.x()
.y()
.do_some_request().await
}
}
#[tokio::main]
async fn main() {
let my_managers = vec![
Arc::new(Mutex::new(SomeStatefulManager{})),
Arc::new(Mutex::new(SomeStatefulManager{})),
];
let mut my_futures = vec![];
for manager in my_managers {
let manager_rc = manager.clone();
my_futures.push(tokio::task::spawn(async move {
let mut man = manager_rc.lock().await;
man.service().await;
}));
}
}
The compiler complains:
error: future cannot be sent between threads safely
--> src/main.rs:62:25
|
62 | my_futures.push(tokio::task::spawn(async move {
| _________________________^
63 | | let mut man = manager_rc.lock().await;
64 | |
65 | | man.service().await;
66 | | }));
| |__________^ future created by async block is not `Send`
|
= help: within `{async block@src/main.rs:62:44: 66:10}`, the trait `Send` is not implemented for `GoofyProperty`, which is required by `{async block@src/main.rs:62:44: 66:10}: Send`
note: future is not `Send` as this value is used across an await
--> src/main.rs:46:32
|
43 | my_program.builder()
| -------------------- has type `GoofyBuilder` which is not `Send`
...
46 | .do_some_request().await
| ^^^^^ await occurs here, with `my_program.builder()` maybe used later
Note how GoofyProperty
is explicitly marked as !Send
and !Sync
. This effectively means that, at the await
point in SomeStatefulManager::service
, my_program.builder()
cannot be shared between objects.
It is clear to me that the await point requires synchronization. I, however, do not understand how to approach this problem — my_program.builder()
is a temporary object. If I were to wrap it in a Mutex
, all I would do is have a MutexGuard
which also isn’t Sync
.
Thanks for the help!