fix(i18n): thread-safe translation for the daemon monitor thread (don't race loadLanguage)
Concurrency audit: I18n::translate() returns a const char* pointing INTO strings_, and loadLanguage() clears/rebuilds strings_ on the main thread at a language switch. The EmbeddedDaemon state/monitor callback builds its status strings via TR() off the main thread, so a concurrent language switch could free the map node out from under that pointer — a use-after-free data race. - Add a shared_mutex guarding strings_: loadLanguage() takes it exclusively for the whole clear+rebuild (its helpers loadBuiltinEnglish/overlayTranslations are called only from there and don't lock, so no recursion); a new translateCopy()/trCopy() takes a shared lock and returns an owned std::string. - The lock-free translate()/TR() stays for main-thread UI (same thread as the writer, so never races). - The four daemon-state setDaemonStatus(TR(...)) calls in the monitor callback now use util::trCopy(...). Synchronization only; no fund-path call touched. Threading fix — clean build + ctest; the race is runtime-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+4
-4
@@ -6089,16 +6089,16 @@ bool App::startEmbeddedDaemon()
|
||||
// NB: this callback runs on EmbeddedDaemon's monitor thread — write via the locked setter.
|
||||
switch (state) {
|
||||
case daemon::EmbeddedDaemon::State::Starting:
|
||||
setDaemonStatus(TR("sb_starting_daemon"));
|
||||
setDaemonStatus(util::trCopy("sb_starting_daemon"));
|
||||
break;
|
||||
case daemon::EmbeddedDaemon::State::Running:
|
||||
setDaemonStatus(TR("sb_dragonxd_running"));
|
||||
setDaemonStatus(util::trCopy("sb_dragonxd_running"));
|
||||
break;
|
||||
case daemon::EmbeddedDaemon::State::Stopping:
|
||||
setDaemonStatus(TR("sb_dragonxd_stopping"));
|
||||
setDaemonStatus(util::trCopy("sb_dragonxd_stopping"));
|
||||
break;
|
||||
case daemon::EmbeddedDaemon::State::Stopped:
|
||||
setDaemonStatus(TR("sb_dragonxd_stopped"));
|
||||
setDaemonStatus(util::trCopy("sb_dragonxd_stopped"));
|
||||
break;
|
||||
case daemon::EmbeddedDaemon::State::Error:
|
||||
setDaemonStatus("Error: " + msg);
|
||||
|
||||
Reference in New Issue
Block a user