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
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_gui → iced_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:
- 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.
- 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.
Environment
tu 1.5.2(installed viacargo install --locked tokenusage)DISPLAY=:0,WAYLAND_DISPLAY=wayland-0)Repro
No window ever visibly renders; the process prints three I/O errors and then panics.
tu dailyworks correctly; onlytu guifails.Observed output
(The three I/O error lines vary between runs — sometimes
Broken pipe (os error 32), sometimesConnection reset by peer (os error 104). Not the root cause.)Backtrace (RUST_BACKTRACE=1, condensed)
Analysis
Frames 10–12 show
tu::mainentering a Tokio runtime and drivingdispatchviablock_on. Frame 9 therefore runs inside an outer Tokio async context.The
guiarm ofdispatchcallstokenusage::gui::run_gui→iced_winit::program::run, and iced constructs its owniced_futures::Runtime<tokio::Runtime, …>(frame 5) — a second Tokio runtime.When iced's event loop exits, that inner Tokio runtime drops. Its
BlockingPool::shutdowncallsReceiver::wait(frame 2), a blocking operation. The outerblock_onscope is still live, so Tokio's nested-blocking guard correctly refuses and panics.Classic "Runtime inside Runtime" pattern.
Suggested fix
For the
guisubcommand path, avoid entering an outer Tokio runtime before handing control to iced. Options:tu::main, branch on the parsed subcommand: if it isgui, calltokenusage::gui::run_guisynchronously without anyRuntime::block_onwrapping, so iced owns the only Tokio runtime.dispatchas-is, but rungui::run_guion a freshly spawnedstd::threadoutside 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.