From 29d62544d6d058e0327a851b76b96c39fcc8c16f Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 5 Feb 2026 16:21:03 +0530 Subject: [PATCH] refactor: improve macOS event handling and TDLib shutdown process - Updated the event handling in the Tauri application to use a match statement for better clarity and maintainability. - Added graceful shutdown logic for TDLib during application exit to prevent crashes related to C++ destructors. - Enhanced the TDLib manager to signal shutdown and exit the receive loop cleanly, improving application stability. --- src-tauri/src/lib.rs | 34 +++++++++++++++++++------ src-tauri/src/services/tdlib/manager.rs | 23 +++++++++++++---- src-tauri/tauri.conf.json | 2 +- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 2c9d94105..767b1fa52 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -574,14 +574,32 @@ pub fn run() { .build(tauri::generate_context!()) .expect("error while building tauri application") .run(|app_handle, event| { - // Handle macOS Dock icon click (reopen event) - #[cfg(target_os = "macos")] - if let RunEvent::Reopen { .. } = event { - show_main_window(app_handle); - } + match event { + // Handle macOS Dock icon click (reopen event) + #[cfg(target_os = "macos")] + RunEvent::Reopen { .. } => { + show_main_window(app_handle); + } - // Suppress unused variable warnings on other platforms - #[cfg(not(target_os = "macos"))] - let _ = (app_handle, event); + // Gracefully shut down TDLib before process exit to prevent + // use-after-free crash in the blocking receive loop. + #[cfg(not(any(target_os = "android", target_os = "ios")))] + RunEvent::Exit => { + log::info!("[app] Exit event received, shutting down TDLib"); + use crate::services::tdlib::TDLIB_MANAGER; + // Signal the TDLib worker to stop. The blocking receive() call + // has a 2-second internal timeout, so we must wait long enough + // for any in-flight call to finish before process teardown runs + // C++ destructors on TDLib's internal state. + TDLIB_MANAGER.signal_shutdown(); + std::thread::sleep(std::time::Duration::from_millis(2500)); + log::info!("[app] TDLib shutdown wait complete"); + let _ = app_handle; + } + + _ => { + let _ = app_handle; + } + } }); } diff --git a/src-tauri/src/services/tdlib/manager.rs b/src-tauri/src/services/tdlib/manager.rs index 195d64130..d8f4969e9 100644 --- a/src-tauri/src/services/tdlib/manager.rs +++ b/src-tauri/src/services/tdlib/manager.rs @@ -179,14 +179,20 @@ impl TdLibManager { let poll_handle = tokio::spawn(async move { loop { if !state_clone.is_active.load(Ordering::SeqCst) { + log::info!("[tdlib] Receive loop exiting (shutdown signalled)"); break; } - // Use spawn_blocking for the blocking TDLib receive call - let receive_result = tokio::task::spawn_blocking(|| { - // Use a short timeout to be more responsive - // Note: tdlib_rs::receive() uses 2.0s internally, but that's OK - // because we're in spawn_blocking and won't block the async runtime + // Clone the Arc so we can check is_active inside + // spawn_blocking *before* entering the 2-second blocking + // td_receive() FFI call. This minimises the window where + // a process-exit could tear down TDLib state while we're + // inside the C++ code. + let state_for_recv = state_clone.clone(); + let receive_result = tokio::task::spawn_blocking(move || { + if !state_for_recv.is_active.load(Ordering::SeqCst) { + return None; + } tdlib_rs::receive() }) .await; @@ -504,6 +510,13 @@ impl TdLibManager { Ok(()) } + /// Signal the TDLib worker to stop (non-async, safe to call from any context). + /// This is used during app exit to prevent the receive loop from crashing + /// when TDLib's C++ static destructors run during process shutdown. + pub fn signal_shutdown(&self) { + self.state.is_active.store(false, Ordering::SeqCst); + } + /// Check if the client is active. pub fn is_active(&self) -> bool { self.state.is_active.load(Ordering::SeqCst) && self.client_id.read().is_some() diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 1c63cf719..a596e65fc 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -41,7 +41,7 @@ "macOS": { "minimumSystemVersion": "10.15", "dmg": { - "background": "./images/background-dmg.svg" + "background": "./images/background-dmg.png" }, "frameworks": [ "./libraries/libtdjson.1.8.29.dylib",