First of all, thanks for all your time making and sharing this library, I started to use it and so far have been very happy with it.
I am learning rust and don't know if this is indeed a BUG bug after asking (https://stackoverflow.com/q/63529755/1135424) and researching seems to be an issue in the implementation of the methods from iter (https://docs.rs/sled/0.34.2/sled/struct.Iter.html) missing Send.
For example, I can reproduce this when trying to use the output of db.iter().values() the coded compiles but if running clippy returns:
future cannot be sent between threads safely
snipped of code:
```rust
let mut tasks = FuturesUnordered::new();
let bin_parts = db_parts.iter().values();
for bin_part in bin_parts {
if let Ok(p) = bin_part {
let part: Part = from_reader(&p[..])?;
tasks.push(async { upload_part(s3, key, file, &upload_id, sdb, part).await });
// limit to N threads (4 for example)
if tasks.len() == threads {
while let Some(r) = tasks.next().await {
if r.is_ok() {
pb.inc(1)
}
}
}
}
}
As a workaround, I am iterating and saving the output into a vector and then using the vector to push the tasks to `FuturesUnordered`
```rust
let mut tasks = FuturesUnordered::new();
let mut xx: Vec<Part> = Vec::new();
{
let bin_parts = db_parts.iter().values();
for bin_part in bin_parts {
if let Ok(p) = bin_part {
let part: Part = from_reader(&p[..])?;
xx.push(part);
}
}
}
for x in xx {
tasks.push(async { upload_part(s3, key, file, &upload_id, sdb, x).await });
// limit to N threads
if tasks.len() == threads {
while let Some(r) = tasks.next().await {
if r.is_ok() {
pb.inc(1)
}
}
}
}
By doing this I don't get anymore the future cannot be sent between threads safely warning but I need to allocate all the output of the tree into a Vector and iterate over the vector and this is something I would like to prevent.
Any better workaround?
Thanks in advance.
Hey @nbari :) The issue of Send not being implemented can be kind of tricky to understand. Here's how I understand this error:
.await because that's where the sub-Future is polled, and that may return Pending, which could cause the executor to move the Future from T1 to T2 during execution.So, the issue is that there is some object in-scope during a call to .await which does not implement Send, while using the Future with an async executor that requires the Future implements Send. We can fix this error by changing any of these facts:
.await. For example:
let rng = rand::thread_rng(); // uses thread-local storage, NOT Send
let gen = rng.gen::<u64>();
my_future.await;
// compile error: Future does not implement Send as rng is in-scope
let gen: u64 = { rand::thread_rng().gen() };
// rng is dropped at the end of the sub-scope above
my_future.await;
// compile success, nothing !Send in-scope at point of yield
This said, sled only has a single async block (fn) currently, and it is Send: Tree::flush_async. So, I think this is a class of errors that sled has simply avoided by not using async blocks, and the only thing that implements Future is also Send: Subscriber.
I suspect that the Send issue may be due to some object that is in-scope during your call to .await. Note that this could be present in an async block several levels above the specific .await call. If a !Send Future .await's a Send Future, the overall Future is still !Send. It only takes a single !Send child Future to make the overall Future !Send, so it's viral.
Feel free to send me a dm on discord if you'd like me to take a closer look at your code where this issue is happening! I may also be wrong, due to my understanding being incomplete, but this is my current expectation.
Hi @spacejam thanks for the reply, I will try to debug deeper also by removing other objects and let you know.
I tried to test removing all possible objects indeed only sending an usize:
let mut tasks = FuturesUnordered::new();
let bin_parts = db_parts.iter().values();
for bin_part in bin_parts {
if let Ok(p) = bin_part {
let _part: Part = from_reader(&p[..])?;
tasks.push(async { upload_part(1).await });
}
}
or also with move and and inner scope:
let tasks = FuturesUnordered::new();
let bin_parts = db_parts.iter().values();
for bin_part in bin_parts {
{
if let Ok(p) = bin_part {
let _part: Part = from_reader(&p[..])?;
let uid = upload_id.clone();
tasks.push(async move { upload_part(s3, key, file, uid, sdb, part).await });
}
}
}
I am testing with this code
the fix for this is released in 0.34.4
Most helpful comment
the fix for this is released in 0.34.4