Panic in channel recv() when combined with reqwest

I'm implementing an HTTP client that periodically reads a web page. My test program for the web access works (not included), and a skeleton for periodic execution also works. But the two parts do not work together. Below is an excerpt from the code. This program works fine.

use std::thread;
use std::sync::mpsc::channel;
use futures::executor::block_on;
use std::time::Duration;

#[tokio::main]
async fn main() {
    let (tx, rx) = channel();

    thread::spawn( move || {
        loop {
            let mess = block_on( task() );
            tx.send(mess).unwrap();
            thread::sleep(Duration::from_millis(1000));
        }
    });

    loop {
        let value = rx.recv().unwrap(); // Hang until signal
        println!("received {:#?}", value );
    }  
}

async fn task() -> String {
/*
    let client = reqwest::Client::new();
    let resp_result;
    resp_result = client
    .get("https://rewo.se")
    .send().await;
*/
    "Hello world".to_string()
}

However, if I uncomment the reqwest call in task(), the program panics in the call to rx.recv().unwrap() with the message:

thread '<unnamed>' panicked at 'there is no reactor running, must be called from the context of a Tokio 1.x runtime'

Apparently, the usage of reqwest affects the execution environment in a way that disturbs channel, but only at recv(), not at send(). Similar problems may be found on the internet, but they seem to be associated with earlier versions of tokio.

I've tested this in Rust Playground, but I experience the same problem in Visual Studio Code, where I have a tokio dependency:
tokio = { version = "1", features = ["full"] }.
Any help is appreciated.

That's strange, since it is expected to be panicking in client.get().send().await - reqwest asynchronous client can't work if it isn't running inside tokio, and you're running it in the futures executor, not in tokio.

You seem to understand the problem right away. But what do I do to solve it?

I'm not sure, since it's highly unusual to mix two runtimes. Do you ever need thread::spawn? Can't you do tokio::spawn with async block instead?

I can definitly use tokio::spawn if it solves the problem.

In this case a Tokio spawn together with Tokio's sleep function should do it, but if you do have a use-case for block_on, then you should use the one from Tokio rather than the one from futures.

I note that using tokio::spawn changes the program structure. There is a problem with the closure and even with channel. Could you direct me to documentation, or even better a tutorial or a code example, where this is shown in detail? Or, maybe, you can add some pseudo code to your comment.

Just replace the closure with an async block and it should work.

I've tested the program more thoroughly, and I see a problem. The program doesn't panic, but it doesn't output anything, as if hanging.

This is what I see in Visual Studio Code. The original program, with thread::spawn and closure and without reqwest, prints one line every second. When I use tokio:spawn and the async block instead, the program still prints one line every second. When I uncomment the reqwest code, the program stops printing anything at all. I observe the same behavior when I run the program from the command line using cargo run.

Rust Playground is not a good environment for these tests because output is always printed only after the program is killed, and the simplest program:

fn main() {
    loop { println!("loop"); }
}

never produces any output at all in Playground.

Should I post a new question for this issue?

If you post the code in question, we can take a look at it.

In this case, I don't think its a big deal whether you open a new question. Either way is fine.

I've found the solution to this problem. tokio::spawn, async block instead of a closure, and an await on the async function task() do the trick. Using block_on() causes disturbances; the loop seems to hang. Perhaps somebody with good understanding of Rust internals can explain the difference between await and block_on().

I thank Alice and Cerber-Ursi for their help. I mark the question as solved (again).

The difference is .await is meant for when you are inside async code, and block_on is meant for when you are not. You might want to check out this article.

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.