Skip to content

tokio@0.2.14 + futures::executor::block_on causes hang #2376

@bryanburgers

Description

@bryanburgers

Version

tokio@0.2.14 and tokio@0.2.15 exhibit this issue, tokio@0.2.13 does not.

tokio-hang-reproduction v0.1.0 (/Users/bryan/personal/tokio-hang-reproduction)
│   │   │   ├── tokio v0.2.14
│   │   │   │   └── tokio-macros v0.2.5
│   │   │   └── tokio-util v0.3.1
│   │   │       └── tokio v0.2.14 (*)
│   │   ├── tokio v0.2.14 (*)
│   ├── tokio v0.2.14 (*)
└── tokio v0.2.14 (*)

Platform

Currently, I can get this to happen on AWS Lambda.

I can not reproduce on MacOS (Darwin Bryans-MacBook-Pro.local 19.3.0 Darwin Kernel Version 19.3.0: Thu Jan 9 20:58:23 PST 2020; root:xnu-6153.81.5~1/RELEASE_X86_64 x86_64) or in Docker-for-Mac.

Description

For our GraphQL server, we use AWS Lambda + Juniper + Tokio. Juniper is currently a synchronous library, but the Lambda library we use and our model layer are fully tokio@0.2.

In order to get Juniper to call our models, we use futures::executor::block_on, based largely on this comment: #2042 (comment)

Starting with tokio@0.2.14, this approach has started hanging and causing timeouts. Downgrading to tokio@0.2.13 solved the issue.

https://github.com/bryanburgers/tokio-hang-reproduction

#![allow(dead_code)]
use futures::stream::{self, StreamExt};
use tokio::time::delay_for;
use std::time::Duration;
use serde_json::{Value, json};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
    let fun = lambda::handler_fn(handler);
    lambda::run(fun).await?;
    Ok(())
}

async fn handler(_event: Value) -> Result<Value, String> {
    let result = invoke_juniper();
    // If we don't use juniper and .await the futures normally, it works fine.
    // let result = test1(100).await;
    println!("result={:?}", result);
    Ok(json!({}))
}

// Juniper (https://crates.io/crates/juniper) is a synchronous library.
fn invoke_juniper() -> Vec<usize> {
    // And from it, we call into our async code using block_on
    futures::executor::block_on(test1(100))
}


// All three of these functions fail after executing ~60 futures.
async fn test1(n: usize) -> Vec<usize> {
    stream::iter(0..n).map(pause_and_return).buffer_unordered(10).collect().await
}

async fn test2(n: usize) -> Vec<usize> {
    stream::iter(0..n).map(pause_and_return).buffered(10).collect().await
}

async fn test3(n: usize) -> Vec<usize> {
    let mut vec = Vec::new();
    for i in 0..n {
        vec.push(pause_and_return(i).await);
    }
    vec
}

async fn pause_and_return(n: usize) -> usize {
    println!("Starting {}", n);
    delay_for(Duration::from_millis(100)).await;
    println!("Finished {}", n);
    n
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-tokioArea: The main tokio crateC-questionUser questions that are neither feature requests nor bug reportsI-hangProgram never terminates, resulting from infinite loops, deadlock, livelock, etc.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions