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.
This commit is contained in:
SoftFever
2026-05-15 19:45:24 +08:00
committed by GitHub
parent cf0dfd12ce
commit 2167378bbe
2 changed files with 24 additions and 21 deletions

View File

@@ -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

View File

@@ -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<int>(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<int>(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<int>(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<OrcaCloudServiceAgent>(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