diff --git a/server/streamreader/process_stream.cpp b/server/streamreader/process_stream.cpp index 86cc3e7d..619020c8 100644 --- a/server/streamreader/process_stream.cpp +++ b/server/streamreader/process_stream.cpp @@ -114,8 +114,8 @@ void ProcessStream::connect() on_connect(); if (wd_timeout_sec_ > 0) { - watchdog_ = make_unique(strand_, this); - watchdog_->start(std::chrono::seconds(wd_timeout_sec_)); + watchdog_ = make_unique(strand_); + watchdog_->start(std::chrono::seconds(wd_timeout_sec_), [this](std::chrono::milliseconds ms) { onTimeout(ms); }); } else { @@ -171,7 +171,7 @@ void ProcessStream::stderrReadLine() } -void ProcessStream::onTimeout(const Watchdog& /*watchdog*/, std::chrono::milliseconds ms) +void ProcessStream::onTimeout(std::chrono::milliseconds ms) { LOG(ERROR, LOG_TAG) << "Watchdog timeout: " << ms.count() / 1000 << "s\n"; if (process_) diff --git a/server/streamreader/process_stream.hpp b/server/streamreader/process_stream.hpp index e4210357..2a19fecb 100644 --- a/server/streamreader/process_stream.hpp +++ b/server/streamreader/process_stream.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2024 Johannes Pohl + Copyright (C) 2014-2025 Johannes Pohl This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,38 +42,43 @@ using boost::asio::posix::stream_descriptor; * Implements EncoderListener to get the encoded data. * Data is passed to the PcmStream::Listener */ -class ProcessStream : public AsioStream, public WatchdogListener +class ProcessStream : public AsioStream { public: - /// ctor. Encoded PCM data is passed to the PipeListener + /// c'tor. Encoded PCM data is passed to the PipeListener ProcessStream(PcmStream::Listener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); + /// d'tor ~ProcessStream() override = default; protected: void connect() override; void disconnect() override; - std::string exe_; - std::string path_; - std::string params_; - bp::pipe pipe_stdout_; - bp::pipe pipe_stderr_; - bp::child process_; + std::string exe_; ///< filename of the process + std::string path_; ///< base path of the provess + std::string params_; ///< parameters for the process + bp::pipe pipe_stdout_; ///< stdout of the process + bp::pipe pipe_stderr_; ///< stderr of the process + bp::child process_; ///< the process - bool logStderr_; - boost::asio::streambuf streambuf_stderr_; - std::unique_ptr stream_stderr_; + bool logStderr_; ///< log stderr to log? + boost::asio::streambuf streambuf_stderr_; ///< stderr read buffer + std::unique_ptr stream_stderr_; ///< stderr stream - // void worker() override; + /// Read async from stderr virtual void stderrReadLine(); + /// Called for a line read from stderr virtual void onStderrMsg(const std::string& line); + /// Try to find exe and base path, throw on error virtual void initExeAndPath(const std::string& filename); + /// @return the executables complete path to @p filename std::string findExe(const std::string& filename) const; - size_t wd_timeout_sec_; - std::unique_ptr watchdog_; - void onTimeout(const Watchdog& watchdog, std::chrono::milliseconds ms) override; + size_t wd_timeout_sec_; ///< Watchdog timeout for arrival of new log lines + std::unique_ptr watchdog_; ///< the watchdog + /// called on wd timeout, kills the process + void onTimeout(std::chrono::milliseconds ms); }; } // namespace streamreader diff --git a/server/streamreader/watchdog.cpp b/server/streamreader/watchdog.cpp index 0976884b..7c934b3a 100644 --- a/server/streamreader/watchdog.cpp +++ b/server/streamreader/watchdog.cpp @@ -36,7 +36,7 @@ using namespace std; namespace streamreader { -Watchdog::Watchdog(const boost::asio::any_io_executor& executor, WatchdogListener* listener) : timer_(executor), listener_(listener) +Watchdog::Watchdog(const boost::asio::any_io_executor& executor) : timer_(executor) { } @@ -47,9 +47,10 @@ Watchdog::~Watchdog() } -void Watchdog::start(const std::chrono::milliseconds& timeout) +void Watchdog::start(const std::chrono::milliseconds& timeout, TimeoutHandler&& handler) { LOG(INFO, LOG_TAG) << "Starting watchdog, timeout: " << std::chrono::duration_cast(timeout).count() << "s\n"; + handler_ = std::move(handler); timeout_ms_ = timeout; trigger(); } @@ -70,7 +71,8 @@ void Watchdog::trigger() if (!ec) { LOG(INFO, LOG_TAG) << "Timed out: " << std::chrono::duration_cast(timeout_ms_).count() << "s\n"; - listener_->onTimeout(*this, timeout_ms_); + if (handler_) + handler_(timeout_ms_); } }); } diff --git a/server/streamreader/watchdog.hpp b/server/streamreader/watchdog.hpp index d5cf4163..0f6139bb 100644 --- a/server/streamreader/watchdog.hpp +++ b/server/streamreader/watchdog.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2024 Johannes Pohl + Copyright (C) 2014-2025 Johannes Pohl This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,27 +32,27 @@ namespace streamreader class Watchdog; -class WatchdogListener -{ -public: - virtual void onTimeout(const Watchdog& watchdog, std::chrono::milliseconds ms) = 0; -}; - - /// Watchdog class Watchdog { public: - Watchdog(const boost::asio::any_io_executor& executor, WatchdogListener* listener = nullptr); + using TimeoutHandler = std::function; + + /// c'tor + explicit Watchdog(const boost::asio::any_io_executor& executor);//, WatchdogListener* listener = nullptr); + /// d'tor virtual ~Watchdog(); - void start(const std::chrono::milliseconds& timeout); + /// start the watchdog, call @p handler on @p timeout + void start(const std::chrono::milliseconds& timeout, TimeoutHandler&& handler); + /// stop the watchdog void stop(); + /// trigger the watchdog (reset timeout) void trigger(); private: boost::asio::steady_timer timer_; - WatchdogListener* listener_; + TimeoutHandler handler_; std::chrono::milliseconds timeout_ms_; };