Tonic/Tokio: Cannot drop a runtime in a context where blocking is not allowed

Hi, I was extracting a service I haven't worked for quite some time and then noticed that upon shutdown that it throws an error saying it cannot drop a runtime.

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 loosely remember having fixed something similar ages ago and I believe it was related to mixing sync and async, but I cannot remember anything anymore.

In a nutshell, the service builds a gRPC service, attaches a gRPC health handler, builds a http service to expose metrics via autometrics and then stats them jointly with a signal handler to listen to shutdown signals. Fairly basic. The main method looks like this:

 dbg_print("Setup autoconfiguration");
    let ctx_manager = async { CtxManager::new() }.await;
    let dns_manager = async { DnsManager::new(&ctx_manager) }.await;
    let cfg_manager = async {
        CfgManager::new(
            SVC_ID,
            smdb_specs::smdb_service_config(),
            &ctx_manager,
            &dns_manager,
        )
    }
    .await;

    dbg_print(&format!("Detected context: {}", ctx_manager.env_type()));

    dbg_print("Auto-configure service ip and port for context");
    let service_addr = cfg_manager
        .get_svc_socket_addr()
        .expect("DBGW: Failed to get host and port");

    dbg_print("Set up socket address for gRPC and HTTP");
    let grpc_addr = service_addr
        .parse()
        .expect("[SMDB]: Failed to parse address");
    dbg_print(&format!("Socket address: {}", service_addr));
  dbg_print("Construct gRPC health_service");
    let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
    health_reporter
        .set_serving::<SmdbServiceServer<SMDBServer>>()
        .await;

    dbg_print("Construct gRPC server");
    let grpc_svc = SmdbServiceServer::new(SMDBServer::new());
    let signal = shutdown_utils::signal_handler("gRPC server");
    let grpc_server = Server::builder()
        .add_service(grpc_svc)
        .add_service(health_service)
        .serve_with_shutdown(grpc_addr, signal);

    dbg_print("Configure http endpoint");
    let (metrics_addr, metrics_uri) = cfg_manager
        .get_metrics_socket_addr_uri()
        .expect("[SMDB]: Failed to get metric host, uri, and port");
    dbg_print(&format!(" metrics_addr: {}", &metrics_addr));
    dbg_print(&format!(" metrics_uri: {}", &metrics_uri));

    dbg_print("Build http metrics endpoint");
    let routes = warp::get()
        .and(warp::path(metrics_uri.clone()))
        .and(warp::path::end())
        .and_then(metrics_handler);

    dbg_print("Build http server");
    let web_addr: SocketAddr = metrics_addr.parse().expect("Failed to parse web address");
    let signal = shutdown_utils::signal_handler("metric server");
    let (_, web_server) = warp::serve(routes).bind_with_graceful_shutdown(web_addr, signal);

    // Create a handler for each server https://github.com/hyperium/tonic/discussions/740
    dbg_print("Create an async handler for gRPC server");
    let grpc_handle = tokio::spawn(grpc_server);
    dbg_print("Create an async handler for http server");
    let http_handle = tokio::spawn(web_server);

    // Start all servers jointly
    dbg_print("Start all servers jointly");
    print_utils::print_start_header(&SVC_ID, &service_addr, &metrics_addr, &metrics_uri);
    match tokio::try_join!(grpc_handle, http_handle) {
        Ok(_) => {}
        Err(e) => {
            println!("[SMDB]: Failed to start gRPC and HTTP server: {:?}", e);
        }
    }

Full code in this repo.

The service starts happily, does it thing, but when I hit ctrl-c, it throws the error that it cannot drop a runtime. From memory, the sync stuff at the very top is all wrapped in an async context to prevent issues with Tokio, but I am not so sure anymore if that is the right way so maybe that is the culprit?

If I were to re-write those mangers all as async, would that solve the issue for good?

What is the stack trace of the panic? That should show you where it's happening.

1 Like

Okay, after inspecting the stacktrace, I saw that it originates in the DnsManager, which has an embedded hickory DNS resolver that seems to use internally a sync Resolver. I think I have to dig in deeper there as there is also an AsyncResolver available so this starts to make sense now.

Full stacktrace below.

hread 'main' panicked at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/blocking/shutdown.rs:51:21:
Cannot drop a runtime in a context where blocking is not allowed. This happens when a runtime is dropped from within an asynchronous context.
stack backtrace:
   0: rust_begin_unwind
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/std/src/panicking.rs:652:5
   1: core::panicking::panic_fmt
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/panicking.rs:72:14
   2: tokio::runtime::blocking::shutdown::Receiver::wait
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/blocking/shutdown.rs:51:21
   3: tokio::runtime::blocking::pool::BlockingPool::shutdown
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/blocking/pool.rs:262:12
   4: <tokio::runtime::blocking::pool::BlockingPool as core::ops::drop::Drop>::drop
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/blocking/pool.rs:283:9
   5: core::ptr::drop_in_place<tokio::runtime::blocking::pool::BlockingPool>
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/ptr/mod.rs:542:1
   6: core::ptr::drop_in_place<tokio::runtime::runtime::Runtime>
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/ptr/mod.rs:542:1
   7: core::ptr::drop_in_place<core::cell::UnsafeCell<tokio::runtime::runtime::Runtime>>
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/ptr/mod.rs:542:1
   8: core::ptr::drop_in_place<std::sync::mutex::Mutex<tokio::runtime::runtime::Runtime>>
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/ptr/mod.rs:542:1
   9: core::ptr::drop_in_place<hickory_resolver::resolver::Resolver>
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/ptr/mod.rs:542:1
  10: core::ptr::drop_in_place<dns_manager::DnsManager>
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/ptr/mod.rs:542:1
  11: smdb::main::{{closure}}
             at ./simplex_services/smdb/smdb_service/src/main.rs:95:1
  12: tokio::runtime::park::CachedParkThread::block_on::{{closure}}
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/park.rs:281:63
  13: tokio::runtime::coop::with_budget
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/coop.rs:107:5
  14: tokio::runtime::coop::budget
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/coop.rs:73:5
  15: tokio::runtime::park::CachedParkThread::block_on
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/park.rs:281:31
  16: tokio::runtime::context::blocking::BlockingRegionGuard::block_on
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/context/blocking.rs:66:9
  17: tokio::runtime::scheduler::multi_thread::MultiThread::block_on::{{closure}}
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/scheduler/multi_thread/mod.rs:87:13
  18: tokio::runtime::context::runtime::enter_runtime
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/context/runtime.rs:65:16
  19: tokio::runtime::scheduler::multi_thread::MultiThread::block_on
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/scheduler/multi_thread/mod.rs:86:9
  20: tokio::runtime::runtime::Runtime::block_on_inner
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/runtime.rs:363:45
  21: tokio::runtime::runtime::Runtime::block_on
             at /Users/marvin/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.39.3/src/runtime/runtime.rs:335:13
  22: smdb::main
             at ./simplex_services/smdb/smdb_service/src/main.rs:94:5
  23: core::ops::function::FnOnce::call_once
             at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

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.