Fix the 3D view stuttering every few seconds while signed in to Orca Cloud

This commit is contained in:
SoftFever
2026-10-10 21:15:45 +08:00
parent e4142db820
commit 53eeedb642
6 changed files with 491 additions and 8 deletions
+29
View File
@@ -1861,6 +1861,17 @@ namespace {
constexpr int FINAL_DRAIN_TIMEOUT_MS = 100; // Final event processing before destruction
constexpr int POLL_INTERVAL_MS = 50; // Polling interval for state checks
constexpr int MAX_YIELD_ITERATIONS = 20; // Maximum wxYield calls per drain cycle
// The Orca agent's health check reports into GUI_App from a worker thread, and the agent can
// outlive GUI_App's use of it (the plugin service keeps a reference), so stop the check before
// the agent is deleted or replaced. The cast is null when no Orca agent is attached.
void stop_orca_health_check(NetworkAgent* agent)
{
if (!agent)
return;
if (auto orca_agent = std::dynamic_pointer_cast<OrcaCloudServiceAgent>(agent->get_cloud_agent()))
orca_agent->stop_health_check();
}
}
// Process pending wx events with bounded iteration count
@@ -1993,6 +2004,10 @@ bool GUI_App::hot_reload_network_plugin()
}
if (m_agent) {
// Stop the health check first: a result it posts after the Phase 2 drain would run while
// m_agent is null or replaced.
stop_orca_health_check(m_agent);
// Phase 1: Clear all callbacks (stops new invocations)
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Phase 1 - clearing callbacks";
m_agent->set_on_ssdp_msg_fn(nullptr);
@@ -2860,6 +2875,7 @@ int GUI_App::OnExit()
NetworkAgentFactory::clear_printer_agent_cache();
if (m_agent) {
stop_orca_health_check(m_agent);
// BBS avoid a crash on mac platform
#ifdef __WINDOWS__
m_agent->start_discovery(false, false);
@@ -2893,6 +2909,14 @@ int GUI_App::OnExit()
return wxApp::OnExit();
}
void GUI_App::CleanUp()
{
// OnExit() does not run when OnInit() fails, and m_agent is then still set. Stop the check before
// wxApp::CleanUp() resets wxTheApp, which a result posted from the worker would still be using.
stop_orca_health_check(m_agent);
wxApp::CleanUp();
}
class wxBoostLog : public wxLog
{
void DoLogText(const wxString &msg) override {
@@ -3115,9 +3139,11 @@ bool GUI_App::on_init_inner()
// OnExit() and ~GUI_App() never run. Shut the plugins and Python down here as ~GUI_App() does. Left to
// PluginManager's static destructor, the shutdown locks hook state that has already been destroyed and aborts.
// Unload the Bambu network plugin too. Its static destructors abort if its agent's threads are still running.
// Stop the Orca Cloud health check as OnExit() does, so no request is in flight while exit() tears down.
wxGetApp().Bind(wxEVT_END_SESSION, [this](wxCloseEvent &e) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "received wxEVT_END_SESSION";
stop_sync_user_preset();
stop_orca_health_check(m_agent);
Slic3r::NetworkAgent::unload_network_module();
Slic3r::PluginManager::instance().shutdown();
Slic3r::PythonInterpreter::instance().shutdown();
@@ -3975,6 +4001,9 @@ bool GUI_App::on_init_network(bool try_backup)
// m_agent = new Slic3r::NetworkAgent(data_directory);
std::unique_ptr<Slic3r::NetworkAgent> agent_ptr = Slic3r::create_agent_from_config(data_directory, app_config);
// A direct restart_networking() replaces m_agent without deleting it, so a health check in flight
// on the old agent would still report into the GUI.
stop_orca_health_check(m_agent);
m_agent = agent_ptr.release();
if (!m_device_manager)
+1
View File
@@ -373,6 +373,7 @@ public:
std::string get_local_models_path();
bool OnInit() override;
int OnExit() override;
void CleanUp() override;
bool initialized() const { return m_initialized; }
inline bool is_enable_multi_machine() { return this->app_config&& this->app_config->get("enable_multi_machine") == "true"; }
#ifdef SLIC3R_CAD
+3
View File
@@ -200,6 +200,9 @@ public:
/**
* Force a server state recheck, clearing any cached state.
* The result arrives through is_server_connected() and OnServerConnectedFn. Implementations may
* return before the check finishes, fold the call into a check already in flight, and ignore
* calls while the agent is shutting down.
*/
virtual int refresh_connection() = 0;
+81 -8
View File
@@ -523,6 +523,7 @@ OrcaCloudServiceAgent::OrcaCloudServiceAgent(std::string log_dir)
OrcaCloudServiceAgent::~OrcaCloudServiceAgent()
{
stop_health_check();
if (refresh_thread.joinable()) {
refresh_thread.join();
}
@@ -963,20 +964,55 @@ bool OrcaCloudServiceAgent::ensure_token_fresh(const std::string& reason) { retu
// ICloudServiceAgent - Server Connectivity
// ============================================================================
int OrcaCloudServiceAgent::connect_server()
// /api/v1/health needs no auth, so unlike the data requests this does no token refresh or 401
// recovery: refresh_connection() runs it off the UI thread, where a refresh could race a logout, and
// it has to stay cancellable.
bool OrcaCloudServiceAgent::run_health_check(const std::atomic_bool* cancel)
{
std::string response;
unsigned int http_code = 0;
int result = http_get(ORCA_HEALTH_PATH, &response, &http_code);
if (cancel && cancel->load())
return false;
bool connected = (result == BAMBU_NETWORK_SUCCESS && http_code >= 200 && http_code < 300);
HttpResult res;
try {
auto http = Http::get(api_base_url + ORCA_HEALTH_PATH);
http.tls_verify(true);
for (const auto& [key, value] : data_headers())
http.header(key, value);
if (cancel)
http.on_progress([cancel](Http::Progress, bool& cancel_request) { cancel_request = cancel->load(); });
http.on_complete([&](std::string body, unsigned status) {
res.success = true;
res.status = status;
res.body = std::move(body);
})
.on_error([&](std::string body, std::string error, unsigned status) {
res.status = status == 0 ? 404 : status; // same mapping as http_get
res.body = std::move(body);
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: health check failed - " << error;
})
.timeout_max(30)
.perform_sync();
} catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: health check exception - " << e.what();
}
// Stopped mid-request: report nothing, the receivers may be going away.
if (cancel && cancel->load())
return false;
const bool connected = res.success;
{
std::lock_guard<std::recursive_mutex> lock(state_mutex);
is_connected = connected;
}
if (!connected)
invoke_http_error_callback(res.status, res.body);
invoke_server_connected_callback(connected ? 0 : -1, res.status);
return connected;
}
invoke_server_connected_callback(connected ? 0 : -1, http_code);
return connected ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_CONNECTION_TO_SERVER_FAILED;
int OrcaCloudServiceAgent::connect_server()
{
return run_health_check(nullptr) ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_CONNECTION_TO_SERVER_FAILED;
}
bool OrcaCloudServiceAgent::is_server_connected()
@@ -985,7 +1021,44 @@ bool OrcaCloudServiceAgent::is_server_connected()
return is_connected;
}
int OrcaCloudServiceAgent::refresh_connection() { return connect_server(); }
// The device manager calls this every 5 s on the UI thread and the GET can take up to its 30 s
// timeout, so the check runs on a worker and reports through is_server_connected() and the
// callbacks. A call while a check is in flight is folded into it. Called from one thread only (the
// UI thread), like stop_health_check().
int OrcaCloudServiceAgent::refresh_connection()
{
if (health_check_stopped.load())
return BAMBU_NETWORK_ERR_CANCELED;
bool expected = false;
if (!health_check_running.compare_exchange_strong(expected, true))
return BAMBU_NETWORK_SUCCESS;
if (health_check_thread.joinable())
health_check_thread.join(); // the previous check is done; only its thread exit is left
try {
health_check_thread = std::thread([this] {
try {
run_health_check(&health_check_stopped);
} catch (...) {
// Callback dispatch (std::function copies, CallAfter) must not terminate the app.
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: health check worker exception";
}
health_check_running.store(false);
});
} catch (...) {
// std::system_error, or std::bad_alloc for the callable: leave the next tick free to retry.
health_check_running.store(false);
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: cannot start health check";
return BAMBU_NETWORK_ERR_CONNECTION_TO_SERVER_FAILED;
}
return BAMBU_NETWORK_SUCCESS;
}
void OrcaCloudServiceAgent::stop_health_check()
{
health_check_stopped.store(true);
if (health_check_thread.joinable())
health_check_thread.join();
}
int OrcaCloudServiceAgent::start_subscribe(std::string module)
{
@@ -206,6 +206,10 @@ public:
int connect_server() override;
bool is_server_connected() override;
int refresh_connection() override;
// Cancels a health check started by refresh_connection() and waits for it; later
// refresh_connection() calls do nothing. Call it before tearing down what the server-connected
// and HTTP-error callbacks reach.
void stop_health_check();
bool is_refresh_running() const { return refresh_running.load(); }
int start_subscribe(std::string module) override;
int stop_subscribe(std::string module) override;
@@ -392,6 +396,10 @@ private:
bool decode_jwt_expiry(const std::string& token, std::chrono::system_clock::time_point& out_tp);
bool should_refresh_locked(std::chrono::seconds skew) const;
// Server reachability probe shared by connect_server() and refresh_connection(); returns
// false without reporting anything once `cancel` is set.
bool run_health_check(const std::atomic_bool* cancel);
// Callback invocation
void invoke_server_connected_callback(int return_code, int reason_code);
void invoke_http_error_callback(unsigned http_code, const std::string& http_body);
@@ -454,6 +462,9 @@ private:
mutable std::recursive_mutex state_mutex;
std::thread refresh_thread;
std::atomic_bool refresh_running{false};
std::thread health_check_thread;
std::atomic_bool health_check_running{false};
std::atomic_bool health_check_stopped{false};
};
} // namespace Slic3r