Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does calling tokio::spawn result in the panic "SpawnError { is_shutdown: true }"?

I want to use Delay to do some work later. If I use tokio::run, it just works fine, but it panics when using tokio::spawn:

use std::sync::mpsc;
use std::time::*;

use tokio::prelude::*; // 0.1.14

fn main() {
    let (tx, rx) = mpsc::channel();
    let task = tokio::timer::Delay::new(Instant::now() + Duration::from_secs(1))
        .map(move |_| {
            tx.send(String::from("hello")).unwrap();
            ()
        })
        .map_err(|e| {
            panic!("{:?}", e);
        });
    tokio::spawn(task);
    let msg = rx.recv().unwrap();
    println!("{}", msg);
}
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: SpawnError { is_shutdown: true }', src/libcore/result.rs:1009:5

I need to use spawn not run if I want various tasks to work concurrently. How to change the code to make it work?

like image 999
hr567 Avatar asked Feb 03 '19 13:02

hr567


1 Answers

The documentation for tokio::spawn states:

This function will panic if the default executor is not set or if spawning onto the default executor returns an error.

Effectively, this means that tokio::spawn should only be called from inside a call to tokio::run.

Since you have only a single future to execute, you might as well just pass it directly to tokio::run. If you had multiple futures, then you can make make use of future::lazy to construct a lazily-evaluated future that will call spawn when it eventually runs:

use std::time::*;
use tokio::prelude::*; // 0.1.14

fn main() {
    tokio::run(futures::lazy(|| {
        tokio::spawn(wait_one_sec().map(|_| println!("One")));
        tokio::spawn(wait_one_sec().map(|_| println!("Two")));
        Ok(())
    }));
}

fn wait_one_sec() -> impl Future<Item = (), Error = ()> {
    tokio::timer::Delay::new(Instant::now() + Duration::from_secs(1))
        .map(drop)
        .map_err(|e| panic!("{:?}", e))
}

Note that if you forget the futures::lazy then you will get the same error. This is because the arguments to functions are evaluated eagerly, which means that the call to tokio::spawn happens first, causing the same sequence of events.

use std::sync::mpsc;

I think it's highly doubtful that you want to use the standard libraries channels, as they are not async-aware and thus will block — a very bad thing in async code.

Instead, you probably want futures::sync::mpsc.

like image 191
Shepmaster Avatar answered Nov 15 '22 09:11

Shepmaster