Search code examples
rustrust-cargorust-tokio

Tokio Cannot drop a runtime in a context where blocking is not allowed. This happens when a runtime is dropped from within an asynchronous context


I have a web scraper which uses rust-bert library for NLP and tokio. I've tried to add #[tokio::main] on top of scrape_article and to add it to main.rs into tokio::task::spawn_blocking but no results I'm getting this error

    Finished dev [unoptimized + debuginfo] target(s) in 14.41s
     Running `target/debug/stock-perception-api`
[2022-08-23T13:57:14Z INFO  stock_perception_api::config::db] Configuring Database...
thread 'main' panicked at 'Cannot drop a runtime in a context where blocking is not allowed. This happens when a runtime is dropped from within an asynchronous context.', /home/r/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.1/src/runtime/blocking/shutdown.rs:51:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fish: Job 1, 'cargo run' terminated by signal SIGSEGV (Address boundary error)

Here is the function which gives the error, more precisely the function get_sentiment

pub async fn scrape_article(conn: &Pool) {
    let stocks_repo = StockRepo::new(conn);
    let sentiment_repo = SentimentRepo::new(conn);
    let article_repo = ArticleRepo::new(conn);

    let count = get_count_of_stocks(stocks_repo).unwrap();

    for item in 1..=count {
        let links = get_all_article_links_by_stock_id(article_repo, item as i32).unwrap();

        for link in links {
            let parsing_result = parse_article(&link).await.unwrap();
            let sentiment: Vec<Sentiment> = get_sentiment(&parsing_result.content).await.unwrap(); ------> Err
            let new_sentiment;

            if sentiment[0].polarity == SentimentPolarity::Negative {
                new_sentiment = SentimentDTO::new(false, sentiment[0].score);
            } else {
                new_sentiment = SentimentDTO::new(true, sentiment[0].score);
            }

            create_sentiment(sentiment_repo, new_sentiment).unwrap();
        }
    }
    ()
}

get_sentiment function

pub async fn get_sentiment(
    article_content: &str,
) -> Result<Vec<Sentiment>, Box<dyn std::error::Error>> {
    let sentiment_classifier: SentimentModel = SentimentModel::new(Default::default())?;
    let input: [&str; 1] = [article_content];

    let output: Vec<Sentiment> = sentiment_classifier.predict(&input);

    println!("{:?}", output[0]);

    Ok(output)
}

main.rs

#[tokio::main]
async fn main() -> io::Result<()> {
    dotenv().ok();
    env_logger::init_from_env(Env::default().default_filter_or("info"));

    let app_host = env::var(constants::APP_HOST).expect("APP_HOST not found.");
    let app_port = env::var(constants::APP_PORT).expect("APP_PORT not found.");
    let app_url = format!("{}:{}", &app_host, &app_port);
    let db_url = env::var(constants::DATABASE_URL).expect("DATABASE_URL not found.");
    let pool = config::db::migrate_and_config_db(&db_url);


    scrape_article(&pool).await;

    HttpServer::new(move || {
        App::new()
            .wrap(Cors::permissive())
            .app_data(web::Data::new(pool.clone()))
            .wrap(Logger::default())
            .wrap(Logger::new("%a %s %{User-Agent}i"))
            .configure(config::app::config_services)
    })
    .bind(app_url)?
    .run()
    .await
}

Solution

  • I've fixed it by using the async example of the NLP library.