From 8c5bd485ddd0759d1ef225d4fa9d9b63c93edd0d Mon Sep 17 00:00:00 2001 From: DanS Date: Tue, 13 Oct 2026 22:03:00 -0500 Subject: [PATCH] fix(lite): probe servers without joining the thread on the UI thread Adding a lite server from the network tab restarted the background server probe immediately. Starting a probe while one is still running joins the previous worker thread, and that join happened on the UI thread, so the frame could stall until the outstanding probe finished. - Adding a server now sets a dirty flag instead of restarting the probe inline; the next frame starts a fresh probe once the current pass has finished, so the UI thread never blocks on a join. - The probe's libcurl transfers honor the cancel flag through a progress callback and abort cleanly, so a probe in flight stops promptly. Co-Authored-By: Claude Opus 4.8 --- src/ui/windows/network_tab.cpp | 4 +++- src/util/lite_server_probe.cpp | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/ui/windows/network_tab.cpp b/src/ui/windows/network_tab.cpp index ec95ad9..463b694 100644 --- a/src/ui/windows/network_tab.cpp +++ b/src/ui/windows/network_tab.cpp @@ -37,6 +37,7 @@ namespace { util::LiteServerProbe s_probe; bool s_probeStarted = false; +bool s_probeDirty = false; bool s_showHidden = false; char s_addUrl[256] = ""; char s_addLabel[128] = ""; @@ -66,6 +67,7 @@ void RenderLiteNetworkTab(App* app) const float dp = Layout::dpiScale(); if (!s_probeStarted) { startProbe(st); s_probeStarted = true; } + if (s_probeDirty && !s_probe.busy()) { startProbe(st); s_probeDirty = false; } const auto probe = s_probe.results(); const bool randomMode = st->getLiteServerSelectionMode() == LiteMode::Random; @@ -221,7 +223,7 @@ void RenderLiteNetworkTab(App* app) servers.push_back(p); st->setLiteServers(servers); st->save(); - startProbe(st); + s_probeDirty = true; // re-probe after the current pass ends (don't join the UI thread here) s_addUrl[0] = '\0'; s_addLabel[0] = '\0'; s_addError.clear(); } } diff --git a/src/util/lite_server_probe.cpp b/src/util/lite_server_probe.cpp index af71bf8..5cb0d0d 100644 --- a/src/util/lite_server_probe.cpp +++ b/src/util/lite_server_probe.cpp @@ -8,11 +8,17 @@ #include +#include #include namespace dragonx { namespace util { +static int liteProbeXferCb(void* p, curl_off_t, curl_off_t, curl_off_t, curl_off_t) +{ + return static_cast*>(p)->load(std::memory_order_relaxed) ? 1 : 0; +} + std::string liteServerHost(const std::string& url) { // Strip scheme ("scheme://") then take everything up to the first '/', '?' or '#'. @@ -69,7 +75,12 @@ void LiteServerProbe::run(std::vector urls) curl_easy_setopt(c, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(c, CURLOPT_SSL_VERIFYHOST, 2L); + curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(c, CURLOPT_XFERINFOFUNCTION, liteProbeXferCb); + curl_easy_setopt(c, CURLOPT_XFERINFODATA, const_cast*>(&cancel_)); + const CURLcode res = curl_easy_perform(c); + if (res == CURLE_ABORTED_BY_CALLBACK) { curl_easy_cleanup(c); break; } if (res == CURLE_OK) { r.online = true; double t = 0.0;