From 9417553c32eaeeb2c24f5273e6f5143784d3f8bb Mon Sep 17 00:00:00 2001 From: Mykola Nahirnyi Date: Fri, 29 May 2026 08:18:01 +0300 Subject: [PATCH] Resolves UI freeze issue by properly treating 409 issue --- src/slic3r/GUI/GUI_App.cpp | 50 +++++++++++++++++++--- src/slic3r/GUI/GUI_App.hpp | 3 +- src/slic3r/Utils/OrcaCloudServiceAgent.cpp | 12 ++++-- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 6b4f5fc89d..a2670f4fd0 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -4875,6 +4876,13 @@ void GUI_App::on_http_error(wxCommandEvent &evt) return; } + // 409 Conflict is expected during normal cloud sync (preset already exists on cloud). + // Treat it as a no-op - the caller handles conflict resolution via sync_push. + if (status == 409) { + BOOST_LOG_TRIVIAL(info) << "Http 409 Conflict — preset already exists on cloud, no dialog needed."; + return; + } + static bool m_is_error_shown = false; // Show general error notification for Orca Cloud API failures (not Bambu) if (provider == ORCA_CLOUD_PROVIDER && status >= 400 && code != HttpErrorVersionLimited) { @@ -5840,7 +5848,7 @@ void GUI_App::push_notification(const MachineObject* obj, wxString msg, wxStrin } } -void GUI_App::reload_settings() +void GUI_App::reload_settings(std::optional> cancel_token) { if (preset_bundle && m_agent) { // Load user's personal presets @@ -5917,10 +5925,42 @@ void GUI_App::reload_settings() if (plater_) plater_->sidebar().update_all_preset_comboboxes(); }; - if (is_main_thread_active()) + if (is_main_thread_active()) { refresh_synced_ui(); - else - CallAfter(refresh_synced_ui); + } else { + // Block the background sync thread until the main thread has applied the + // cloud-sourced setting_ids back into local presets. Without this wait, + // the periodic push loop starts immediately with stale sync_info="create" + // / empty setting_ids and re-uploads every preset, getting a 409 from the + // cloud for each one (it already has them from the pull that just finished). + auto p = std::make_shared>(); + std::future fut = p->get_future(); + CallAfter([refresh_synced_ui = std::move(refresh_synced_ui), p]() mutable { + try { + refresh_synced_ui(); + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "reload_settings: refresh_synced_ui threw: " << e.what(); + } catch (...) { + BOOST_LOG_TRIVIAL(error) << "reload_settings: refresh_synced_ui threw unknown exception"; + } + p->set_value(); // always unblock the sync thread + }); + // Poll every 100 ms so we can bail out on cancellation (app close) + // without blocking stop_sync_user_preset()'s join() for up to 10 s. + auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(10); + while (fut.wait_for(std::chrono::milliseconds(100)) == std::future_status::timeout) { + if (cancel_token.has_value() && cancel_token->expired()) { + BOOST_LOG_TRIVIAL(warning) << "reload_settings: cancelled while waiting for UI " + "preset update — sync loop may see stale setting_ids"; + break; + } + if (std::chrono::steady_clock::now() >= deadline) { + BOOST_LOG_TRIVIAL(warning) << "reload_settings: timed out waiting for UI " + "preset update — sync loop may see stale setting_ids"; + break; + } + } + } } } @@ -6699,7 +6739,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg) finishFn(ret == 0); if (ret == 0 && m_agent && !t.expired()) - reload_settings(); + reload_settings(t); // For orca specific syncing auto orca_agent = std::dynamic_pointer_cast(m_agent->get_cloud_agent()); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 5b6bcc6581..e14b41b0aa 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -2,6 +2,7 @@ #define slic3r_GUI_App_hpp_ #include +#include #include #include "ImGuiWrapper.hpp" #include "ConfigWizard.hpp" @@ -519,7 +520,7 @@ public: std::string format_IP(const std::string& ip); void show_dialog(wxString msg); void push_notification(const MachineObject* obj, wxString msg, wxString title = wxEmptyString, UserNotificationStyle style = UserNotificationStyle::UNS_NORMAL); - void reload_settings(); + void reload_settings(std::optional> cancel_token = std::nullopt); void remove_user_presets(); bool maybe_migrate_user_presets_on_login(); diff --git a/src/slic3r/Utils/OrcaCloudServiceAgent.cpp b/src/slic3r/Utils/OrcaCloudServiceAgent.cpp index 0e1027cd46..5417fad995 100644 --- a/src/slic3r/Utils/OrcaCloudServiceAgent.cpp +++ b/src/slic3r/Utils/OrcaCloudServiceAgent.cpp @@ -500,10 +500,14 @@ int OrcaCloudServiceAgent::start() { regenerate_pkce(); - // Attempt silent sign-in from stored refresh token + // Attempt silent sign-in from stored refresh token. + // Use async=true so the HTTP round-trip does not block the main UI thread. + // start_sync_user_preset() guards with is_user_login(), so if the refresh has + // not completed by the time it is called the sync simply defers to the next + // login-complete callback — safe and preferred over a 30-second UI stall. std::string stored_refresh; if (load_refresh_token(stored_refresh) && !stored_refresh.empty()) { - refresh_now(stored_refresh, "refresh token", false); + refresh_now(stored_refresh, "startup_refresh", true); } return BAMBU_NETWORK_SUCCESS; @@ -1888,7 +1892,7 @@ int OrcaCloudServiceAgent::http_post(const std::string& path, const std::string& .on_error([&](std::string resp_body, std::string error, unsigned resp_status) { result.success = false; result.status = resp_status == 0 ? 404 : resp_status; - result.body = body; + result.body = resp_body; BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: HTTP error - " << error; }) .timeout_max(30) @@ -1958,7 +1962,7 @@ int OrcaCloudServiceAgent::http_put(const std::string& path, const std::string& .on_error([&](std::string resp_body, std::string error, unsigned resp_status) { result.success = false; result.status = resp_status == 0 ? 404 : resp_status; - result.body = body; + result.body = resp_body; BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: HTTP error - " << error; }) .timeout_max(30)