Skip to content

Commit 4706a78

Browse files
committed
desktop: Properly shutdown the tokio runtime before exiting the winit event loop
Fixes issue #22308.
1 parent 1030e93 commit 4706a78

File tree

2 files changed

+39
-10
lines changed

2 files changed

+39
-10
lines changed

desktop/src/app.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,16 +388,23 @@ impl MainWindow {
388388

389389
pub struct App {
390390
main_window: Option<MainWindow>,
391+
runtime: Option<tokio::runtime::Runtime>,
391392
gilrs: Option<Gilrs>,
392393
event_loop_proxy: EventLoopProxy<RuffleEvent>,
393394
preferences: GlobalPreferences,
394395
font_database: fontdb::Database,
395396
}
396397

398+
/// Enters the tokio runtime context.
399+
/// This cannot be a method, as the borrow-checker would complain.
400+
macro_rules! enter_runtime {
401+
($this:expr) => {
402+
let _guard = $this.runtime.as_ref().map(|runtime| runtime.enter());
403+
};
404+
}
405+
397406
impl App {
398-
pub async fn new(
399-
preferences: GlobalPreferences,
400-
) -> Result<(Self, EventLoop<RuffleEvent>), Error> {
407+
pub fn new(preferences: GlobalPreferences) -> Result<(Self, EventLoop<RuffleEvent>), Error> {
401408
let event_loop = EventLoop::with_user_event().build()?;
402409

403410
let mut font_database = fontdb::Database::default();
@@ -409,10 +416,12 @@ impl App {
409416
})
410417
.ok();
411418
let event_loop_proxy = event_loop.create_proxy();
419+
let runtime = tokio::runtime::Runtime::new()?;
412420

413421
Ok((
414422
Self {
415423
main_window: None,
424+
runtime: Some(runtime),
416425
gilrs,
417426
event_loop_proxy,
418427
font_database,
@@ -425,6 +434,8 @@ impl App {
425434

426435
impl ApplicationHandler<RuffleEvent> for App {
427436
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
437+
enter_runtime!(self);
438+
428439
if cause == StartCause::Init {
429440
let movie_url = self.preferences.cli.movie_url.clone();
430441
let icon_bytes = include_bytes!("../assets/favicon-32.rgba");
@@ -533,6 +544,8 @@ impl ApplicationHandler<RuffleEvent> for App {
533544
fn resumed(&mut self, _event_loop: &ActiveEventLoop) {}
534545

535546
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: RuffleEvent) {
547+
enter_runtime!(self);
548+
536549
match (&mut self.main_window, event) {
537550
(Some(main_window), RuffleEvent::TaskPoll(task)) => main_window.player.poll(task),
538551

@@ -636,12 +649,16 @@ impl ApplicationHandler<RuffleEvent> for App {
636649
_window_id: WindowId,
637650
event: WindowEvent,
638651
) {
652+
enter_runtime!(self);
653+
639654
if let Some(main_window) = &mut self.main_window {
640655
main_window.window_event(event_loop, event);
641656
}
642657
}
643658

644659
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
660+
enter_runtime!(self);
661+
645662
if let Some(main_window) = &mut self.main_window {
646663
main_window.about_to_wait(self.gilrs.as_mut());
647664

@@ -655,6 +672,21 @@ impl ApplicationHandler<RuffleEvent> for App {
655672
}
656673
}
657674
}
675+
676+
fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
677+
// `MainWindow` needs a tokio context to properly drop.
678+
{
679+
enter_runtime!(self);
680+
let _ = self.main_window.take();
681+
}
682+
683+
// Manually stop the tokio runtime: this makes sure that any pending Player-bound futures
684+
// are properly cancelled and put back on the winit event loop before it closes, preventing
685+
// them from being dropped on the wrong thread and causing a panic.
686+
if let Some(runtime) = self.runtime.take() {
687+
runtime.shutdown_timeout(std::time::Duration::from_secs(1));
688+
}
689+
}
658690
}
659691

660692
enum LoadingState {

desktop/src/main.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,7 @@ fn shutdown() {
151151
}
152152
}
153153

154-
#[tokio::main]
155-
async fn main() -> Result<(), Error> {
154+
fn main() -> Result<(), Error> {
156155
init();
157156

158157
let opt = Opt::parse();
@@ -191,11 +190,9 @@ async fn main() -> Result<(), Error> {
191190

192191
subscriber.init();
193192

194-
let result = App::new(preferences)
195-
.await
196-
.and_then(|(mut app, event_loop)| {
197-
event_loop.run_app(&mut app).context("Event loop failure")
198-
});
193+
let result = App::new(preferences).and_then(|(mut app, event_loop)| {
194+
event_loop.run_app(&mut app).context("Event loop failure")
195+
});
199196

200197
#[cfg(windows)]
201198
if let Err(error) = &result {

0 commit comments

Comments
 (0)