From 2167378bbe55903aea056cd4ef8c5cf3c4856f15 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Fri, 15 May 2026 19:45:24 +0800 Subject: [PATCH] Enhancement: eliminate UI freeze during cloud preset sync on startup (#13673) fix: eliminate UI freeze during cloud preset sync on startup Move synchronous HTTP calls off the main thread in GUI_App::start_sync_user_preset(): - Call scan_orphaned_info_files() and process_delete_presets() in the background sync thread instead of the UI thread, so their HTTP DELETE calls don't block startup. - Call reload_settings() directly from the background thread instead of via CallAfter, so get_user_presets() (HTTP GET) and load/save_user_presets (file I/O) run off the main thread. - Guard update_side_preset_ui() and app_config->save() with is_main_thread_active() / CallAfter so they're safe when called from a worker thread. - Simplifiy finishFn lambdas: the progress-dialog case only destroys the dialog; the no-dialog case is a no-op. --- src/libslic3r/AppConfig.cpp | 4 +++- src/slic3r/GUI/GUI_App.cpp | 41 +++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 49afa106be..55918e86c2 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -834,8 +834,10 @@ std::string AppConfig::load() void AppConfig::save() { - if (! is_main_thread_active()) + if (!is_main_thread_active()) { + BOOST_LOG_TRIVIAL(fatal) << "Calling AppConfig::save() from a worker thread!"; throw CriticalException("Calling AppConfig::save() from a worker thread!"); + } // The config is first written to a file with a PID suffix and then moved // to avoid race conditions with multiple instances of Slic3r diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b8b36861a8..38c9199d56 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -5782,7 +5782,10 @@ void GUI_App::reload_settings() load_pending_vendors(); preset_bundle->load_user_presets(*app_config, user_presets, ForwardCompatibilitySubstitutionRule::Enable); preset_bundle->save_user_presets(*app_config, get_delete_cache_presets()); - mainframe->update_side_preset_ui(); + if (is_main_thread_active()) + mainframe->update_side_preset_ui(); + else + CallAfter([this] { mainframe->update_side_preset_ui(); }); } } @@ -6023,8 +6026,10 @@ void GUI_App::load_pending_vendors() return; preset_bundle->apply_vendor_config(need_add_vendors, need_add_filaments, app_config, false); - app_config->save(); - + if (is_main_thread_active()) + app_config->save(); + else + CallAfter([this] { app_config->save(); }); need_add_vendors.clear(); need_add_filaments.clear(); } @@ -6508,36 +6513,29 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg) cancelFn = [this, dlg]() { return is_closing() || dlg->WasCanceled(); }; - finishFn = [this, userid = m_agent->get_user_id(), dlg, t = std::weak_ptr(m_user_sync_token)](bool ok) { - CallAfter([=]{ - dlg->Destroy(); - if (ok && m_agent && t.lock() == m_user_sync_token && userid == m_agent->get_user_id()) reload_settings(); - }); + finishFn = [this, dlg](bool) { + CallAfter([=]{ dlg->Destroy(); }); }; } else { - finishFn = [this, userid = m_agent->get_user_id(), t = std::weak_ptr(m_user_sync_token)](bool ok) { - CallAfter([=] { - if (ok && m_agent && t.lock() == m_user_sync_token && userid == m_agent->get_user_id()) reload_settings(); - }); - }; + finishFn = [](bool) {}; // reload_settings() is now triggered from the background thread cancelFn = [this]() { return is_closing(); }; } - // Do a one-time scan for files that may be pending deletion (e.g., was deleted while not connected to internet) - // Scan for orphaned .info files on startup and add them to deletion queue - scan_orphaned_info_files(); - process_delete_presets(); - Bind(EVT_UPDATE_PRESET_BUNDLE,&GUI_App::update_single_bundle,this); m_sync_update_thread = Slic3r::create_thread( [this, progressFn, cancelFn, finishFn, t = std::weak_ptr(m_user_sync_token)] { + if (!m_agent) return; + + // One-time scan for orphaned .info files left over from offline deletions; queues HTTP DELETEs. + scan_orphaned_info_files(); + process_delete_presets(); + // get setting list, update setting list std::string version = preset_bundle->get_vendor_profile_version(PresetBundle::ORCA_DEFAULT_BUNDLE).to_string(); - if(!m_agent) return; // run check_and_fix_user_presets_syncinfo once before syncing to make sure all presets have correct sync_info // So that we can sync presets that are migrated from old version or users manually put preset files in preset folder @@ -6562,9 +6560,12 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg) } }, progressFn, cancelFn); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " get_setting_list2 ret = " << ret << " m_is_closing = " << m_is_closing; - + finishFn(ret == 0); + if (ret == 0 && m_agent && !t.expired()) + reload_settings(); + // For orca specific syncing auto orca_agent = std::dynamic_pointer_cast(m_agent->get_cloud_agent()); int tick_tock = -1, sync_count = 0; // tick_tock = -1 to immediately run sync the frist time this thread runs