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

Is it possible to use LocalSet from within a spawned task? #2095

Closed
Alovchin91 opened this issue Jan 11, 2020 · 4 comments
Closed

Is it possible to use LocalSet from within a spawned task? #2095

Alovchin91 opened this issue Jan 11, 2020 · 4 comments

Comments

@Alovchin91
Copy link

Alovchin91 commented Jan 11, 2020

Version

tokio-macros v0.2.3
tokio v0.2.9

Platform

MacOS 10.15.2:

Darwin 19.2.0
Darwin Kernel Version 19.2.0: Sat Nov 9 03:47:04 PST 2019; root:xnu-6153.61.1~20/RELEASE_X86_64 x86_64

Description

I can't find a way to use LocalSet inside a task spawned by tokio::spawn inside a #[tokio::main] main fn.

I tried this code:

#[tokio::main]
async fn main() {
    tokio::spawn(async move {
        let local = tokio::task::LocalSet::new();
        local.run_until(async {}).await;
    }).await.unwrap()
}

In this case, the compiler complains that:

rror[E0277]: `std::rc::Rc<tokio::task::local::Scheduler>` cannot be sent between threads safely
   --> src/main.rs:3:5
    |
3   |     tokio::spawn(async move {
    |     ^^^^^^^^^^^^ `std::rc::Rc<tokio::task::local::Scheduler>` cannot be sent between threads safely
    | 
   ::: /Users/me/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.9/src/task/spawn.rs:123:21
    |
123 |         T: Future + Send + 'static,
    |                     ---- required by this bound in `tokio::task::spawn::spawn`
    |
    = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<tokio::task::local::Scheduler>`
    = note: required because it appears within the type `tokio::task::local::LocalSet`
    = note: required because it appears within the type `{tokio::task::local::LocalSet, tokio::task::local::LocalSet, tokio::task::local::LocalSet, ()}`
    = note: required because it appears within the type `[static generator@src/main.rs:3:29: 6:6 {tokio::task::local::LocalSet, tokio::task::local::LocalSet, tokio::task::local::LocalSet, ()}]`
    = note: required because it appears within the type `std::future::GenFuture<[static generator@src/main.rs:3:29: 6:6 {tokio::task::local::LocalSet, tokio::task::local::LocalSet, tokio::task::local::LocalSet, ()}]>`
    = note: required because it appears within the type `impl std::future::Future`

From my understanding, it tries to capture a Scheduler from the outer context (the main function). (And Rc is obviously !Send.)

The only other way to run LocalSet that I know of is to call LocalSet::block_on, but it requires a Runtime which I don't think is possible to get from inside a context of #[tokio::main], and also creating a new Runtime is not possible because I'm already in a context of a runtime.

Also, if I try to remove await and just pass the Future returned by LocalSet::run_until to tokio::spawn like this:

tokio::spawn({
    let local = tokio::task::LocalSet::new();
    local.run_until(async {})
}).await.unwrap()

then the compiler panics: rust-lang/rust#68109

What I'm trying to achieve is to run Actix inside Tokio runtime in a separate thread so I could send it a shutdown signal at some point.

Do you think there is a way to run LocalSet inside Tokio-spawned task? Or am I getting the whole idea of LocalSet wrong? 😬

Thanks!

@carllerche
Copy link
Member

You cannot use regular tokio::spawn as spawned futures migrate across threads thus require Send. You can do something like:

#[tokio::main]
async fn main() {
    tokio::spawn_blocking(move {
        let rt = tokio::runtime::Handle::current();
        rt.block_on(async {
            let local = tokio::task::LocalSet::new();
            local.run_until(async {}).await;
        });
    }).await.unwrap()
}

@Alovchin91
Copy link
Author

Alovchin91 commented Jan 11, 2020

Thank you for the explanation, @carllerche!

Unfortunately, latest version of tokio::runtime::Handle doesn't have the block_on method: https://docs.rs/tokio/0.2.9/tokio/runtime/struct.Handle.html

How can I do the same with the current API? Thanks!

@carllerche
Copy link
Member

Oh sorry... I thought we were going to add it. Somebody still needs to write that PR :)

You can do this instead:

let rt = runtime::Handle::current();
rt.enter(|| {
    futures::executor::block_on(...)
})

@Alovchin91
Copy link
Author

Thank you! Does exactly what I need 🙂 Hopefully there'll be a corresponding PR for block_on 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants