Skip to content

tu gui panics on exit: Tokio runtime dropped inside async context (v1.5.2) #2

@DanielMarchukov

Description

@DanielMarchukov

Environment

  • tu 1.5.2 (installed via cargo install --locked tokenusage)
  • Ubuntu 22.04 on WSL2 (WSLg, Wayland socket present, DISPLAY=:0, WAYLAND_DISPLAY=wayland-0)
  • Rust toolchain from rustup (stable)

Repro

tu gui

No window ever visibly renders; the process prints three I/O errors and then panics.

tu daily works correctly; only tu gui fails.

Observed output

Io error: Broken pipe (os error 32)
Io error: Broken pipe (os error 32)
Io error: Broken pipe (os error 32)

thread 'main' panicked at /…/tokio-1.49.0/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.

(The three I/O error lines vary between runs — sometimes Broken pipe (os error 32), sometimes Connection reset by peer (os error 104). Not the root cause.)

Backtrace (RUST_BACKTRACE=1, condensed)

 0: rust_begin_unwind
 1: core::panicking::panic_fmt
 2: tokio::runtime::blocking::shutdown::Receiver::wait
 3: tokio::runtime::blocking::pool::BlockingPool::shutdown
 4: core::ptr::drop_in_place<tokio::runtime::blocking::pool::BlockingPool>
 5: core::ptr::drop_in_place<
      iced_futures::runtime::Runtime<
        tokio::runtime::runtime::Runtime,
        iced_winit::proxy::Proxy<tokenusage::gui::Message>,
        iced_runtime::Action<tokenusage::gui::Message>>>
 6: core::ptr::drop_in_place<iced_winit::program::run_instance<…>>
 7: iced_winit::program::run
 8: tokenusage::gui::run_gui
 9: tokenusage::dispatch::{{closure}}
10: tokio::runtime::park::CachedParkThread::block_on
11: tokio::runtime::context::runtime::enter_runtime
12: tu::main

Analysis

Frames 10–12 show tu::main entering a Tokio runtime and driving dispatch via block_on. Frame 9 therefore runs inside an outer Tokio async context.

The gui arm of dispatch calls tokenusage::gui::run_guiiced_winit::program::run, and iced constructs its own iced_futures::Runtime<tokio::Runtime, …> (frame 5) — a second Tokio runtime.

When iced's event loop exits, that inner Tokio runtime drops. Its BlockingPool::shutdown calls Receiver::wait (frame 2), a blocking operation. The outer block_on scope is still live, so Tokio's nested-blocking guard correctly refuses and panics.

Classic "Runtime inside Runtime" pattern.

Suggested fix

For the gui subcommand path, avoid entering an outer Tokio runtime before handing control to iced. Options:

  1. In tu::main, branch on the parsed subcommand: if it is gui, call tokenusage::gui::run_gui synchronously without any Runtime::block_on wrapping, so iced owns the only Tokio runtime.
  2. Or keep dispatch as-is, but run gui::run_gui on a freshly spawned std::thread outside any Tokio context (std::thread::spawn(run_gui).join()).

Either removes the nested runtime and the panic.

Happy to test a fix against my setup.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions