From 7e7804e577fcbeea67488ef6f9aaae2b7f7c5487 Mon Sep 17 00:00:00 2001 From: DanS Date: Wed, 14 Oct 2026 06:26:13 -0500 Subject: [PATCH] perf(daemon): skip the UI-thread datadir lock wait Starting the daemon polls for an already-running node holding the datadir, sleeping up to three 100 ms intervals to let a shutting-down node clear out. When a start runs on the UI thread, that is up to 300 ms of blocking sleep on the render thread. - The lock-wait poll count is now a parameter. A start invoked on the UI thread passes 0, which does a single check and never sleeps; every other caller keeps the three-poll wait. The default is unchanged, so background starts behave exactly as before. Co-Authored-By: Claude Opus 4.8 --- src/app.cpp | 3 ++- src/daemon/daemon_controller.cpp | 4 ++-- src/daemon/daemon_controller.h | 2 +- src/daemon/embedded_daemon.cpp | 5 ++--- src/daemon/embedded_daemon.h | 5 ++++- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index 32ecbec..1e54a6b 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -5975,7 +5975,8 @@ bool App::startEmbeddedDaemon() // Only a process we actually launched starts a new story: a start that finds a node already running (and // launches nothing) must not wipe what the tracker knows about that node from its debug.log. - const bool launched = daemon_controller_->start(settings_.get()); + const bool launched = daemon_controller_->start( + settings_.get(), std::this_thread::get_id() == ui_thread_id_ ? 0 : daemon::EmbeddedDaemon::kDatadirLockWaitMaxPolls); if (launched) resetDaemonBusy(); // a new process: whatever the last one was doing is over return launched; } diff --git a/src/daemon/daemon_controller.cpp b/src/daemon/daemon_controller.cpp index ccabd7f..67be627 100644 --- a/src/daemon/daemon_controller.cpp +++ b/src/daemon/daemon_controller.cpp @@ -41,10 +41,10 @@ void DaemonController::syncSettings(const config::Settings* settings) daemon_->setWalletFile(walletFile); } -bool DaemonController::start(const config::Settings* settings) +bool DaemonController::start(const config::Settings* settings, int lockWaitPolls) { syncSettings(settings); - return daemon_->start(); + return daemon_->start("", lockWaitPolls); } void DaemonController::stop(int waitMs) diff --git a/src/daemon/daemon_controller.h b/src/daemon/daemon_controller.h index 6bbf219..70865e4 100644 --- a/src/daemon/daemon_controller.h +++ b/src/daemon/daemon_controller.h @@ -95,7 +95,7 @@ public: void setStateCallback(StateCallback callback); void syncSettings(const config::Settings* settings); - bool start(const config::Settings* settings); + bool start(const config::Settings* settings, int lockWaitPolls = 3); void stop(int waitMs); void markStopRequested(); diff --git a/src/daemon/embedded_daemon.cpp b/src/daemon/embedded_daemon.cpp index 71a0f3a..f08ab91 100644 --- a/src/daemon/embedded_daemon.cpp +++ b/src/daemon/embedded_daemon.cpp @@ -505,7 +505,7 @@ static bool isPortInUse(int port) #endif } -bool EmbeddedDaemon::start(const std::string& binary_path) +bool EmbeddedDaemon::start(const std::string& binary_path, int lockWaitPolls) { std::lock_guard life(lifecycle_mutex_); @@ -564,11 +564,10 @@ bool EmbeddedDaemon::start(const std::string& binary_path) // skip_port_check_ / -datadir override) are exempt; they run their own datadir+port. { constexpr int kDatadirLockWaitPollMs = 100; - constexpr int kDatadirLockWaitMaxPolls = 3; // ~300ms total, breaks early on exit bool stillRunning = false; if (!skip_port_check_ && override_datadir_.empty()) { stillRunning = isDaemonProcessRunning(); - for (int i = 0; stillRunning && i < kDatadirLockWaitMaxPolls; ++i) { + for (int i = 0; stillRunning && i < lockWaitPolls; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(kDatadirLockWaitPollMs)); stillRunning = isDaemonProcessRunning(); } diff --git a/src/daemon/embedded_daemon.h b/src/daemon/embedded_daemon.h index f979a56..bfade6a 100644 --- a/src/daemon/embedded_daemon.h +++ b/src/daemon/embedded_daemon.h @@ -61,7 +61,10 @@ public: * @return true if process started successfully (or is already running); false while a stop * session (beginStop() .. endStop()) is still in progress */ - bool start(const std::string& binary_path = ""); + static constexpr int kDatadirLockWaitMaxPolls = 3; // ~300ms total, breaks early on exit + + /** @param lockWaitPolls max 100ms datadir-lock waits (0 = single check, never sleeps; for the UI thread) */ + bool start(const std::string& binary_path = "", int lockWaitPolls = kDatadirLockWaitMaxPolls); /** * @brief Stop the daemon process this object launched (never any other dragonxd)