Skip to content
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

Future non-Send although non-Send local is dropped before .await #104883

Closed
Tracked by #69663
hniksic opened this issue Nov 25, 2022 · 14 comments
Closed
Tracked by #69663

Future non-Send although non-Send local is dropped before .await #104883

hniksic opened this issue Nov 25, 2022 · 14 comments
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@hniksic
Copy link
Contributor

hniksic commented Nov 25, 2022

Rust 1.65 doesn't compile this code:

fn main() {
    tokio::spawn(async move {
        let non_send = get_non_send();
        drop(non_send);
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    });
}

fn get_non_send() -> Box<dyn std::io::Read> {
    unimplemented!()
}

Playground

It complains that the future is not Send, reporting the following:

error: future cannot be sent between threads safely
   --> src/main.rs:4:18
    |
4   |       tokio::spawn(async move {
    |  __________________^
5   | |         let non_send = get_non_send();
6   | |         drop(non_send);
7   | |         tokio::time::sleep(std::time::Duration::from_secs(1)).await;
8   | |     });
    | |_____^ future created by async block is not `Send`
    |
    = help: the trait `Send` is not implemented for `dyn std::io::Read`
note: future is not `Send` as this value is used across an await
   --> src/main.rs:7:62
    |
5   |         let non_send = get_non_send();
    |             -------- has type `Box<dyn std::io::Read>` which is not `Send`
6   |         drop(non_send);
7   |         tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    |                                                              ^^^^^^ await occurs here, with `non_send` maybe used later
8   |     });
    |     - `non_send` is later dropped here
note: required by a bound in `tokio::spawn`
   --> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.22.0/src/task/spawn.rs:163:21
    |
163 |         T: Future + Send + 'static,
    |                     ^^^^ required by this bound in `tokio::spawn`

I would expect it to compile, since the offending non-Send value is unconditionally dropped before .await.

If I change drop(non_send) to a block that makes it go out of scope, then it compiles:

    tokio::spawn(async move {
        {
            let non_send = get_non_send();
        }
        tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    });

Playground

As shown, the first example can be trivially transformed to use a scope, but in other situations it might not be as easy. This report is inspired by a post on reddit that described this issue in a slightly more complex scenario.

@hniksic hniksic added the C-bug Category: This is a bug. label Nov 25, 2022
@wwylele
Copy link
Contributor

wwylele commented Nov 25, 2022

possibly duplicate of #104442, though this one has a smaller example

If I understand it correctly this is related to #97331

@dtolnay dtolnay added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-async-await Area: Async & Await labels Nov 25, 2022
@bstrie
Copy link
Contributor

bstrie commented Nov 25, 2022

Here's a smaller repro that doesn't require a tokio dependency:

fn main() {
    spawn(async {
        let non_send: Option<*mut ()> = None;
        drop(non_send);
        std::future::ready(1).await;
    });
}

fn spawn(_: impl Send) {}

@eholk
Copy link
Contributor

eholk commented Dec 19, 2022

We've added this to our tracking issue, #69663.

This looks like something that -Z drop-tracking should fix.

@rustbot label AsyncAwait-triaged

@rustbot rustbot added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label Dec 19, 2022
@timmclean
Copy link

I just ran into this bug too. Note that the compiler message is misleading:

5   |         let non_send = get_non_send();
    |             -------- has type `Box<dyn std::io::Read>` which is not `Send`
6   |         drop(non_send);
7   |         tokio::time::sleep(std::time::Duration::from_secs(1)).await;
    |                                                              ^^^^^^ await occurs here, with `non_send` maybe used later
8   |     });
    |     - `non_send` is later dropped here

The compiler states that non_send is dropped at line 8, but non_send is actually dropped on line 6. Ideally, this code should compile, but at the very least, I think the compiler message should be fixed.

@StyMaar
Copy link

StyMaar commented Mar 28, 2024

This issue looks like it's solved now.

@hniksic
Copy link
Contributor Author

hniksic commented Mar 28, 2024

@StyMaar I can confirm it's solved. Both the original tokio code and the minimal code from @bstrie's comment now compile.

@tbu-
Copy link
Contributor

tbu- commented May 14, 2024

This should be closed then.

@hniksic hniksic closed this as completed May 14, 2024
@MHashirShahzad
Copy link

I get the same error

@hniksic
Copy link
Contributor Author

hniksic commented Jun 24, 2024

@HashirShazad What version of Rust are you using? What example fails, exactly?

@MHashirShahzad
Copy link

MHashirShahzad commented Jun 24, 2024

@hniksic i am using rustc 1.79.0 (129f3b996 2024-06-10)

for table in ... {
   ...
   if scan_subfolders{
      drop(table);
      Box::pin(get_table(url.to_string() + href_attr, folder,
         download_pdfs, download_imgs, scan_subfolders)).await?; // await here
   }
}

It says that await occurs here, with table maybe used later

Full Error

error: future cannot be sent between threads safely
   --> src/main.rs:197:1
    |
197 |   #[tauri::command(rename_all = "snake_case")]
    |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `url_entered` is not `Send`
...
214 | /             tauri::generate_handler![
215 | |                 // all commands go here
216 | |                 url_entered          
217 | |             ]
    | |_____________- in this macro invocation
    |
    = help: within `ego_tree::Node<Node>`, the trait `Sync` is not implemented for `std::cell::Cell<NonZero<usize>>`, which is required by `impl futures::Future<Output = Result<String, ()>>: std::marker::Send`
    = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
note: future is not `Send` as this value is used across an await
   --> src/main.rs:99:93
    |
43  |     for table in html.select(&table_selector){ // get all tables from html
    |         ----- has type `ElementRef<'_>` which is not `Send`
...
99  |                                             download_pdfs, download_imgs, scan_subfolders)).await?; // Call get_table function with new url
    |                                                                                             ^^^^^ await occurs here, with `table` maybe used later
note: required by a bound in `ResultFutureTag::future`
   --> C:\Users\DELL\.cargo\registry\src\index.crates.io-1cd66030c949c28d\tauri-1.6.8\src\command.rs:293:42
    |
289 |     pub fn future<T, E, F>(self, value: F) -> impl Future<Output = Result<Value, InvokeError>>
    |            ------ required by a bound in this associated function
...
293 |       F: Future<Output = Result<T, E>> + Send,
    |                                          ^^^^ required by this bound in `ResultFutureTag::future`
    = note: this error originates in the macro `__cmd__url_entered` which comes from the expansion of the macro `tauri::generate_handler` (in Nightly builds, run with -Z macro-backtrace for more info)

@hniksic
Copy link
Contributor Author

hniksic commented Jun 24, 2024

@HashirShazad Given the code shown is incomplete, it's hard to tell whether this is the same bug, or whether it is a bug at all. I recommend that you distill your code into a minimal example that reproduces the issue (ideally without external dependencies, or with those present on the Playground), and file a new issue.

@MHashirShahzad
Copy link

i will open a new issue tommorow.

@yhx-12243
Copy link

yhx-12243 commented Aug 3, 2024

I think this issue should be reopen because the following simple snippets still fails:

struct NonSend(*const ());

impl NonSend {
	fn noop(&self) {}
}

fn main() {
	assert_send(async {
		let x = NonSend(core::ptr::null());
		x.noop();
		drop(x);
		async {}.await;
	});
}

fn assert_send(_: impl Send) {}
rustc -Vv

rustc 1.82.0-nightly (fd8d6fbe5 2024-08-02)
binary: rustc
commit-hash: fd8d6fbe505ecf913f5e2ca590c69a7da2789879
commit-date: 2024-08-02
host: aarch64-apple-darwin
release: 1.82.0-nightly
LLVM version: 19.1.0

@jieyouxu
Copy link
Member

jieyouxu commented Aug 5, 2024

I think this issue should be reopen because the following simple snippets still fails:

Please open a new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests