@@ -302,26 +302,23 @@ impl Builder {
302302 } )
303303 . on_event ( |app, event| {
304304 match event {
305- RunEvent :: ExitRequested { api , code, .. } => {
305+ RunEvent :: ExitRequested { code, .. } => {
306306 // Only intercept user-initiated exits (code is None). Programmatic
307- // exits via app_handle.exit() have Some(code) — let those through
308- // to avoid an infinite ExitRequested loop.
307+ // exits via app_handle.exit() have Some(code) — let those through.
309308 if code. is_some ( ) {
310309 return ;
311310 }
312311
313312 info ! ( "App exit requested - cleaning up transactions and databases" ) ;
314313
315- // Prevent immediate exit so we can close connections and checkpoint WAL
316- api. prevent_exit ( ) ;
317-
318- let app_handle = app. clone ( ) ;
319-
314+ // NOTE: We intentionally do NOT call api.prevent_exit() here.
315+ // The cleanup below runs synchronously (thread spawn + join),
316+ // so it completes before this handler returns. After returning,
317+ // the event loop will proceed with the exit naturally.
320318 let handle = match tokio:: runtime:: Handle :: try_current ( ) {
321319 Ok ( h) => h,
322320 Err ( _) => {
323321 warn ! ( "No tokio runtime available for cleanup" ) ;
324- app_handle. exit ( code. unwrap_or ( 0 ) ) ;
325322 return ;
326323 }
327324 } ;
@@ -378,8 +375,6 @@ impl Builder {
378375 if let Err ( e) = cleanup_result {
379376 error ! ( "Database cleanup thread panicked: {:?}" , e) ;
380377 }
381-
382- app_handle. exit ( code. unwrap_or ( 0 ) ) ;
383378 }
384379 RunEvent :: Exit => {
385380 // ExitRequested should have already closed all databases
0 commit comments