diff --git a/src/daemon/xmrig_manager.cpp b/src/daemon/xmrig_manager.cpp index 0ef57c7..1235e76 100644 --- a/src/daemon/xmrig_manager.cpp +++ b/src/daemon/xmrig_manager.cpp @@ -356,12 +356,6 @@ void XmrigManager::stop(int wait_ms) { // Try graceful termination first TerminateProcess(process_handle_, 0); WaitForSingleObject(process_handle_, wait_ms); - CloseHandle(process_handle_); - process_handle_ = nullptr; - } - if (stdout_read_) { - CloseHandle(stdout_read_); - stdout_read_ = nullptr; } #else if (process_pid_ > 0) { @@ -386,15 +380,29 @@ void XmrigManager::stop(int wait_ms) { } process_pid_ = 0; } +#endif + + if (monitor_thread_.joinable()) + monitor_thread_.join(); + + // Close the pipe only after the monitor thread has finished with it (it + // drains stdout); closing earlier let read() hit a closed/reused fd. +#ifdef _WIN32 + if (process_handle_) { + CloseHandle(process_handle_); + process_handle_ = nullptr; + } + if (stdout_read_) { + CloseHandle(stdout_read_); + stdout_read_ = nullptr; + } +#else if (stdout_fd_ >= 0) { close(stdout_fd_); stdout_fd_ = -1; } #endif - if (monitor_thread_.joinable()) - monitor_thread_.join(); - state_ = State::Stopped; DEBUG_LOGF("[INFO] XmrigManager: stopped\n"); } @@ -475,6 +483,7 @@ double XmrigManager::getMemoryUsageMB() const { } void XmrigManager::drainOutput() { + std::lock_guard io_lk(io_mutex_); if (!stdout_read_) return; char buf[4096]; DWORD avail = 0; @@ -565,7 +574,7 @@ double XmrigManager::getMemoryUsageMB() const { #ifdef __APPLE__ // macOS: use ps to read RSS for xmrig process char cmd[128]; - snprintf(cmd, sizeof(cmd), "ps -o rss= -p %d 2>/dev/null", process_pid_); + snprintf(cmd, sizeof(cmd), "ps -o rss= -p %d 2>/dev/null", (int)process_pid_.load(std::memory_order_relaxed)); FILE* fp = popen(cmd, "r"); if (!fp) return 0.0; char line[64]; @@ -578,7 +587,7 @@ double XmrigManager::getMemoryUsageMB() const { return mb; #else char path[64]; - snprintf(path, sizeof(path), "/proc/%d/statm", process_pid_); + snprintf(path, sizeof(path), "/proc/%d/statm", (int)process_pid_.load(std::memory_order_relaxed)); FILE* fp = fopen(path, "r"); if (!fp) return 0.0; long dummy = 0, pages = 0; @@ -592,6 +601,7 @@ double XmrigManager::getMemoryUsageMB() const { } void XmrigManager::drainOutput() { + std::lock_guard io_lk(io_mutex_); if (stdout_fd_ < 0) return; char buf[4096]; while (true) { @@ -702,9 +712,8 @@ void XmrigManager::monitorProcess() { // ============================================================================ void XmrigManager::pollStats() { - // No-op on UI thread — stats are fetched by the monitor thread. - // Just drain stdout so log lines stay fresh. - drainOutput(); + // No-op on UI thread — stats and the stdout drain both run on the monitor + // thread (a second reader here raced stop() closing the fd). } void XmrigManager::fetchStatsHttp() { diff --git a/src/daemon/xmrig_manager.h b/src/daemon/xmrig_manager.h index 03c3014..cc240c5 100644 --- a/src/daemon/xmrig_manager.h +++ b/src/daemon/xmrig_manager.h @@ -177,12 +177,13 @@ private: // Process handles #ifdef _WIN32 - HANDLE process_handle_ = nullptr; - HANDLE stdout_read_ = nullptr; + std::atomic process_handle_{nullptr}; + std::atomic stdout_read_{nullptr}; #else - pid_t process_pid_ = 0; - int stdout_fd_ = -1; + std::atomic process_pid_{0}; + std::atomic stdout_fd_{-1}; #endif + std::mutex io_mutex_; // serializes drainOutput(); lock order io_mutex_ -> output_mutex_ std::thread monitor_thread_; std::atomic should_stop_{false};