diff --git a/include/utils/simple_json.h b/include/utils/simple_json.h index 1235d21..9b9767b 100644 --- a/include/utils/simple_json.h +++ b/include/utils/simple_json.h @@ -80,11 +80,12 @@ public: return static_cast(AsNumber(static_cast(def))); } - const std::string& AsString(const std::string& def = EmptyString()) const { + // NOTE: Return by value to avoid lifetime issues when callers pass temporaries as defaults. + std::string AsString(std::string_view def = {}) const { if (const auto* s = std::get_if(&value_)) { return *s; } - return def; + return std::string(def); } const Array& AsArray() const { @@ -134,11 +135,6 @@ public: private: Variant value_; - - static const std::string& EmptyString() { - static const std::string empty; - return empty; - } }; inline void SkipWhitespace(std::string_view text, size_t& pos) { diff --git a/src/graph_manager.cpp b/src/graph_manager.cpp index a06b649..7c95af6 100644 --- a/src/graph_manager.cpp +++ b/src/graph_manager.cpp @@ -1142,29 +1142,34 @@ bool GraphManager::BuildFromFile(const std::string& path, std::string& err) { } bool GraphManager::StartAll() { - std::lock_guard lock(graphs_mu_); + std::scoped_lock lock(mu_, graphs_mu_); + if (running_) return true; + + // Start all graphs; on failure, stop any already started graphs so we never leave a partially-running state. + std::vector started; + started.reserve(graphs_.size()); for (auto& g : graphs_) { + if (!g) continue; if (!g->Start()) { + for (auto* sg : started) { + if (sg) sg->Stop(); + } return false; } + started.push_back(g.get()); } - { - std::lock_guard lock(mu_); - running_ = true; - } + + running_ = true; return true; } void GraphManager::StopAll() { { - std::lock_guard lock(mu_); + std::scoped_lock lock(mu_, graphs_mu_); if (!running_) return; running_ = false; - } - { - std::lock_guard lock(graphs_mu_); for (auto& g : graphs_) { - g->Stop(); + if (g) g->Stop(); } } cv_.notify_all();