fix(daemon): supervise a daemon restarted after a crash
After an unexpected exit the monitor thread returned on its own but stayed joinable, and start() only spawned a monitor when none was joinable. A node restarted after a crash (no stop() in between) then ran unwatched: a second crash was never detected, its output was never drained, and the dead run's stdout pipe leaked into the next fork. - start() joins a monitor that returned on its own (only when joinable, at the top, after the "already running" early returns), closes the dead run's stdout pipe (Windows: process and pipe handles), and always starts a new monitor for the process it launched. crash_count_ is kept. - New lifecycle_mutex_ serializes start(), beginStop(), stop()'s flag writes and joins, and endStop(), so two threads can never join the same monitor. The monitor thread and the state callback never take it; a beginStop() racing a start() waits for it to return (its longest wait is the ~300 ms datadir-lock poll). - start() is refused (returns false, state unchanged) while a stop is in progress, i.e. between beginStop() and endStop(). - The daemon tests now start the stand-in only in isolation (temp -datadir override, port check skipped): they no longer write the real ~/.hush/DRAGONX/obsidiandragon-dragonxd.pid or tail the real debug.log. New tests: restart after a crash is supervised, a restarted run stops cleanly, no fd leak over crash+restart cycles (Linux), and start() is refused while stopping. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,7 +53,8 @@ public:
|
||||
/**
|
||||
* @brief Start the embedded dragonxd daemon
|
||||
* @param binary_path Path to dragonxd binary (auto-detect if empty)
|
||||
* @return true if process started successfully
|
||||
* @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 = "");
|
||||
|
||||
@@ -501,6 +502,9 @@ private:
|
||||
std::thread monitor_thread_;
|
||||
std::atomic<bool> should_stop_{false};
|
||||
std::atomic<bool> stop_requested_{false}; // a stop was asked for: the next exit is not a crash
|
||||
// Serializes start/beginStop/stop/endStop over monitor_thread_, should_stop_ and stop_requested_ (two
|
||||
// threads joining one std::thread is undefined). Never taken by the monitor thread or the state callback.
|
||||
std::mutex lifecycle_mutex_;
|
||||
std::set<std::string> debug_categories_;
|
||||
int max_connections_ = 0; // 0 = daemon default
|
||||
bool stratum_enabled_ = false; // -stratum: host a RandomX pool (v1.3.0+; older daemons ignore it)
|
||||
|
||||
Reference in New Issue
Block a user