diff --git a/CMakeLists.txt b/CMakeLists.txt index 6030f24..e1db94c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ set(SOURCE_FILES ${SRC}/main.cpp ${SRC}/main.hpp ${SRC}/Args.hpp ${SRC}/Args.cpp ${SRC}/Client.cpp ${SRC}/Client.hpp ${SRC}/Controller.cpp ${SRC}/Controller.hpp - ${SRC}/FanThread.cpp ${SRC}/FanThread.hpp + ${SRC}/FanTask.cpp ${SRC}/FanTask.hpp ${SRC}/Devices.cpp ${SRC}/Devices.hpp ${SRC}/FanInterface.cpp ${SRC}/FanInterface.hpp ${SRC}/SensorInterface.cpp ${SRC}/SensorInterface.hpp diff --git a/debian/changelog b/debian/changelog index bb921f7..3e243cd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -fancon (0.23.2) UNRELEASED; urgency=low +fancon (0.23.3) UNRELEASED; urgency=low * Initial release. Closes: #00000 diff --git a/debian/fancon-resume.service b/debian/fancon-resume.service index b596d2b..7ffdec0 100644 --- a/debian/fancon-resume.service +++ b/debian/fancon-resume.service @@ -6,7 +6,7 @@ After=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate [Service] Type=oneshot User=root -ExecStart=/usr/bin/fancon reload +ExecStart=/usr/bin/fancon recover [Install] WantedBy=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target diff --git a/proto/DevicesSpec.proto b/proto/DevicesSpec.proto index c9bfe27..2e709b0 100644 --- a/proto/DevicesSpec.proto +++ b/proto/DevicesSpec.proto @@ -107,5 +107,6 @@ service DService { rpc DisableAll(Empty) returns (Empty) {} rpc Test(TestRequest) returns (stream TestResponse) {} rpc Reload(Empty) returns (Empty) {} + rpc Recover(Empty) returns (Empty) {} rpc NvInit(Empty) returns (Empty) {} } diff --git a/src/Args.cpp b/src/Args.cpp index 97b379d..3b9c923 100644 --- a/src/Args.cpp +++ b/src/Args.cpp @@ -9,3 +9,5 @@ fc::Arg::Arg(string name, string short_name, bool has_value, bool needs_value, bool fc::Arg::has_value() const { return !value.empty(); } fc::Arg::operator bool() const { return triggered; } + +pair fc::Args::a(Arg &arg) { return {arg.key, arg}; } diff --git a/src/Args.hpp b/src/Args.hpp index 5a17f20..0cf652c 100644 --- a/src/Args.hpp +++ b/src/Args.hpp @@ -36,23 +36,17 @@ class Args { service = {"service"}, daemon = {"daemon"}, stop_service = {"stop-service"}, sysinfo = {"sysinfo", "i", true, true, DEFAULT_SYSINFO_PATH}, - nv_init = {"nv-init"}, verbose = {"verbose", "v"}, trace = {"trace", "a"}; - - map from_key = {{help.key, help}, - {status.key, status}, - {enable.key, enable}, - {disable.key, disable}, - {test.key, test}, - {force.key, force}, - {reload.key, reload}, - {config.key, config}, - {service.key, service}, - {daemon.key, daemon}, - {stop_service.key, stop_service}, - {sysinfo.key, sysinfo}, - {nv_init.key, nv_init}, - {verbose.key, verbose}, - {trace.key, trace}}; + recover = {"recover"}, nv_init = {"nv-init"}, verbose = {"verbose", "v"}, + trace = {"trace", "a"}; + + map from_key = { + a(help), a(status), a(enable), a(disable), + a(test), a(force), a(reload), a(config), + a(service), a(daemon), a(stop_service), a(sysinfo), + a(recover), a(nv_init), a(verbose), a(trace)}; + +private: + static pair a(Arg &arg); }; } // namespace fc diff --git a/src/Client.cpp b/src/Client.cpp index 5275265..891d912 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -39,6 +39,8 @@ void fc::Client::run(Args &args) { reload(); } else if (args.stop_service) { stop_service(); + } else if (args.recover) { + recover(); } else if (args.nv_init) { nv_init(); } else if (args.sysinfo) { @@ -199,6 +201,12 @@ void fc::Client::reload() { LOG(llvl::error) << "Failed to reload"; } +void fc::Client::recover() { + ClientContext context; + if (!check(client->Recover(&context, empty, &empty))) + LOG(llvl::error) << "Failed to init nvidia"; +} + void fc::Client::nv_init() { ClientContext context; if (!check(client->NvInit(&context, empty, &empty))) @@ -268,6 +276,8 @@ void fc::Client::print_help(const string &conf) { << " stop-service Stop the service" << endl << "i sysinfo [file] Save system info to file (default: " << fc::DEFAULT_SYSINFO_PATH << ")" << endl + << " recover Recover control of enabled devices" + << endl << " nv-init Init nvidia devices" << endl << "v verbose Debug logging level" << endl << "a trace Trace logging level" << endl; diff --git a/src/Client.hpp b/src/Client.hpp index 7708ffa..400721f 100644 --- a/src/Client.hpp +++ b/src/Client.hpp @@ -35,6 +35,7 @@ class Client { void test(bool forced); void test(const string &flabel, bool forced); void reload(); + void recover(); void nv_init(); void sysinfo(const string &p); diff --git a/src/Controller.cpp b/src/Controller.cpp index 39d7f06..00d240a 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -9,15 +9,15 @@ uint temp_averaging_intervals = 8; } // namespace fc fc::Controller::Controller(path conf_path_) : config_path(move(conf_path_)) { - load_conf_and_enumerated(); + reload(); watcher = spawn_watcher(); } fc::Controller::~Controller() { disable_all(); } FanStatus fc::Controller::status(const string &flabel) const { - const auto it = fthreads.find(flabel); - if (it == fthreads.end()) + const auto it = tasks.find(flabel); + if (it == tasks.end()) return FanStatus::FanStatus_Status_DISABLED; return (it->second.is_testing()) ? FanStatus::FanStatus_Status_TESTING @@ -25,10 +25,8 @@ FanStatus fc::Controller::status(const string &flabel) const { } void fc::Controller::enable(fc::FanInterface &f, bool enable_all_dell) { - if (fthreads.count(f.label) > 0) { - LOG(llvl::trace) << f << ": already enabled"; + if (tasks.count(f.label) > 0) return; - } // Dell fans can only be enabled/disabled togther if (f.type() == DevType::DELL && enable_all_dell) @@ -37,11 +35,12 @@ void fc::Controller::enable(fc::FanInterface &f, bool enable_all_dell) { if (!f.pre_start_check()) return; - fthreads.try_emplace(f.label, [this, &f](bool &run) { + tasks.emplace(f.label, [this, &f](bool &run) { while (run) { notify_status_observers(f.label); f.update(); } + f.disable_control(); }); LOG(llvl::trace) << f.label << ": enabled"; @@ -49,23 +48,21 @@ void fc::Controller::enable(fc::FanInterface &f, bool enable_all_dell) { void fc::Controller::enable_all() { for (auto &[key, f] : devices.fans) { - if (!fthreads.contains(f->label)) + if (!tasks.contains(f->label)) enable(*f); } } void fc::Controller::disable(const string &flabel, bool disable_all_dell) { - const auto it = fthreads.find(flabel); - if (it == fthreads.end()) { - LOG(llvl::error) << flabel << ": failed to find to disable"; + const auto it = tasks.find(flabel); + if (it == tasks.end()) return; - } // Dell fans can only be enabled/disabled togther if (devices.fans.at(flabel)->type() == DevType::DELL && disable_all_dell) return disable_dell_fans(); - fthreads.erase(it); + tasks.erase(it); if (const auto fit = devices.fans.find(flabel); fit != devices.fans.end()) fit->second->disable_control(); @@ -75,10 +72,10 @@ void fc::Controller::disable(const string &flabel, bool disable_all_dell) { void fc::Controller::disable_all() { vector disabled_fans; - for (const auto &[flabel, fthread] : fthreads) + for (const auto &[flabel, task] : tasks) disabled_fans.push_back(flabel); - fthreads.clear(); + tasks.clear(); for (const auto &flabel : disabled_fans) notify_status_observers(flabel); @@ -86,70 +83,66 @@ void fc::Controller::disable_all() { } void fc::Controller::reload() { - // Remove all threads not testing - vector stopped; - for (auto &[key, fthread] : fthreads) { - if (!fthread.is_testing()) - stopped.push_back(key); - } + LOG(llvl::info) << "Reloading changes"; - // TODO: fix devices being overwritten during testing; causing SEGFAULT! - // TODO: reload ONLY modified - side-stepping this issue - // stop all, then restart the fans currently testing? - for (const string &key : stopped) - fthreads.erase(key); + Devices enumerated(true); + merge(enumerated, false); - // Restart - LOG(llvl::info) << "Reloading changes"; - load_conf_and_enumerated(); - enable_all(); + if (const auto c = read_config(); c) { + from(c->config()); + + Devices conf_devs(c->devices()); + merge(conf_devs, true, true); + + remove_devices_not_in({{enumerated}, {conf_devs}}); + } else { + remove_devices_not_in({{enumerated}}); + } + + notify_devices_observers(); } -void fc::Controller::reload_added() { - // TODO: - // Load config changes - load_conf_and_enumerated(); - // Compare new devices to old - // Interrupt and enable ed - // Interrupt all threads not testing, and don't wait - for (auto &[key, fthread] : fthreads) { - if (!fthread.is_testing()) - fthread.t.interrupt(); +void fc::Controller::recover() { + // Re-enable control for all running tasks + for (const auto &[flabel, t] : tasks) { + const auto it = devices.fans.find(flabel); + if (it != devices.fans.end()) + it->second->enable_control(); } } void fc::Controller::nv_init() { #ifdef FANCON_NVIDIA_SUPPORT if (NV::init(true)) - reload(); // TODO: reload only added devices -#endif // FANCON_NVIDIA_SUPPORT + reload(); +#endif // FANCON_NVIDIA_SUPPORT } -void fc::Controller::test(fc::FanInterface &fan, bool forced, - function cb) { +void fc::Controller::test(fc::FanInterface &fan, bool forced, bool blocking, + shared_ptr> test_status) { if (fan.ignore || (fan.tested() && !forced)) return; // If a test is already running for the device then just join onto it - if (auto it = fthreads.find(fan.label); it->second.is_testing()) { - it->second.test_status->register_observer(cb); - it->second.join(); + if (auto it = tasks.find(fan.label); it->second.is_testing()) { + // Add test_status observers to existing test_status + for (const auto &cb : test_status->observers) + it->second.test_status->register_observer(cb); + if (blocking) + it->second.join(); return; } LOG(llvl::info) << fan << ": testing"; // Remove any running thread before testing - if (const auto it = fthreads.find(fan.label); it != fthreads.end()) - fthreads.erase(it); - - auto test_status = make_shared>(0); - test_status->register_observer(cb); + if (const auto it = tasks.find(fan.label); it != tasks.end()) + tasks.erase(it); auto test_func = [&] { // Test fan, then remove thread from map fan.test(*test_status); - fthreads.erase(fan.label); + tasks.erase(fan.label); LOG(llvl::info) << fan << ": test complete"; // Only write to file when no other fan are still testing @@ -160,20 +153,21 @@ void fc::Controller::test(fc::FanInterface &fan, bool forced, }; auto [it, success] = - fthreads.try_emplace(fan.label, move(test_func), test_status); + tasks.try_emplace(fan.label, move(test_func), test_status); if (!success) - test(fan, forced, cb); + test(fan, forced, blocking, test_status); notify_status_observers(fan.label); - it->second.join(); + if (blocking) + it->second.join(); } size_t fc::Controller::tests_running() const { auto f = [](const size_t sum, const auto &p) { return sum + int(p.second.is_testing()); }; - return std::accumulate(fthreads.begin(), fthreads.end(), 0, f); + return std::accumulate(tasks.begin(), tasks.end(), 0, f); } void fc::Controller::set_devices(const fc_pb::Devices &devices_) { @@ -184,33 +178,6 @@ void fc::Controller::set_devices(const fc_pb::Devices &devices_) { enable_all(); } -void fc::Controller::from(const fc_pb::Controller &c) { - from(c.config()); - fc::Devices import_devices; - import_devices.from(c.devices()); - - for (auto &[key, f] : import_devices.fans) { - // Insert or assign or overwrite any existing device sharing that hw_id - const string hw_id = f->hw_id(); - auto it = find_if(devices.fans.begin(), devices.fans.end(), - [&](auto &p) { return p.second->hw_id() == hw_id; }); - if (it != devices.fans.end()) - it->second = move(f); - else - devices.fans.insert_or_assign(key, move(f)); - } - - for (auto &[key, s] : import_devices.sensors) { - const string hw_id = s->hw_id(); - auto it = find_if(devices.sensors.begin(), devices.sensors.end(), - [&](auto &p) { return p.second->hw_id() == hw_id; }); - if (it != devices.sensors.end()) - it->second = move(s); - else - devices.sensors.insert_or_assign(key, move(s)); - } -} - void fc::Controller::from(const fc_pb::ControllerConfig &c) { update_interval = milliseconds(c.update_interval()); dynamic = c.dynamic(); @@ -246,27 +213,103 @@ void fc::Controller::disable_dell_fans() { } } -void fc::Controller::load_conf_and_enumerated() { - devices = fc::Devices(true); +bool fc::Controller::is_testing(const string &flabel) const { + const auto it = tasks.find(flabel); + return it != tasks.end() && it->second.is_testing(); +} - if (config_path.empty() || !exists(config_path)) { - LOG(llvl::debug) << "No config found"; - return; - } +optional fc::Controller::read_config() { + if (!exists(config_path)) + return nullopt; + fc_pb::Controller c; std::ifstream ifs(config_path); std::stringstream ss; ss << ifs.rdbuf(); - - fc_pb::Controller c; google::protobuf::TextFormat::ParseFromString(ss.str(), &c); + // c.ParseFromIstream(&ifs); + update_config_write_time(); - if (ifs) { - update_config_write_time(); - from(c); - notify_devices_observers(); - } else { - LOG(llvl::error) << "Failed to read config from: " << config_path; + if (!ifs) { + LOG(llvl::debug) << "Failed to read config"; + return nullopt; + } + + return c; +} + +void fc::Controller::merge(Devices &d, bool replace_on_match, bool deep_cmp) { + const auto m = [&](auto &src, auto &dst, const auto &on_match) { + for (auto &[key, dev] : src) { + // Insert or assign or overwrite any existing device sharing that hw_id + const string hw_id = dev->hw_id(); + auto it = find_if(dst.begin(), dst.end(), [&](const auto &p) { + return p.second->hw_id() == hw_id; + }); + + const bool match = it != dst.end(); + if (match) { + if (replace_on_match && (!deep_cmp || !dev->deep_equal(*it->second))) { + on_match(it, it->first, key, dev); + } + } else { + dst.emplace(key, move(dev)); + } + } + }; + + m(d.fans, devices.fans, + [&](auto &old_it, const string &old_key, const string &new_key, auto &dev) { + // On match; re-insert device as the key may have changed + const auto re_insert = [&] { + devices.fans.erase(old_it); + devices.fans.emplace(new_key, move(dev)); + }; + const FanStatus fstatus = status(old_key); + if (fstatus == FanStatus::FanStatus_Status_DISABLED) { + re_insert(); + } else if (fstatus == FanStatus::FanStatus_Status_ENABLED) { + disable(old_it->first, false); + re_insert(); + enable(*old_it->second, true); + } else if (fstatus == FanStatus::FanStatus_Status_TESTING) { + const auto task_it = tasks.find(old_key); + const auto test_status = task_it->second.test_status; + disable(old_key, false); + re_insert(); + test(*old_it->second, true, false, test_status); + } + }); + + m(d.sensors, devices.sensors, + [&](auto &old_it, [[maybe_unused]] const string &old_key, + const string &new_key, auto &dev) { + // On match; re-insert device as the key may have changed + devices.sensors.erase(old_it); + devices.sensors.emplace(new_key, move(dev)); + }); +} + +void fc::Controller::remove_devices_not_in( + std::initializer_list> l) { + // Remove items not in conf_devs or enumerated but in devices + for (const auto &p : devices.fans) { + const auto &flabel = std::get<0>(p); + if (!std::any_of(l.begin(), l.end(), [&](const Devices &l) { + return l.fans.contains(flabel); + })) { + disable(flabel); + devices.fans.erase(flabel); + } + } + + for (const auto &p : devices.sensors) { + const auto &slabel = std::get<0>(p); + if (!std::any_of(l.begin(), l.end(), [&](const Devices &l) { + return l.sensors.contains(slabel); + })) { + devices.sensors.erase(slabel); + } } } @@ -311,9 +354,12 @@ bool fc::Controller::config_file_modified() const { thread fc::Controller::spawn_watcher() { return thread([this] { + update_config_write_time(); while (true) { - if (config_file_modified()) + if (config_file_modified()) { + update_config_write_time(); reload(); + } sleep_for(update_interval); } }); diff --git a/src/Controller.hpp b/src/Controller.hpp index 3e2b09e..69d5d4d 100644 --- a/src/Controller.hpp +++ b/src/Controller.hpp @@ -2,7 +2,7 @@ #define FANCON_CONTROLLER_HPP #include "Devices.hpp" -#include "FanThread.hpp" +#include "FanTask.hpp" #include "Util.hpp" #include "proto/DevicesSpec.pb.h" #include @@ -17,13 +17,12 @@ #include using fc::FanInterface; -using fc::FanThread; +using fc::FanTask; using std::find_if; using std::future; using std::istringstream; using std::list; using FanStatus = fc_pb::FanStatus_Status; -using FThreads_map = std::map; using DevicesCallback = function; using StatusCallback = function; @@ -40,7 +39,7 @@ class Controller { ~Controller(); Devices devices; - FThreads_map fthreads; + map tasks; list device_observers; list status_observers; Util::RemovableMutex device_observers_mutex, status_observers_mutex; @@ -51,13 +50,13 @@ class Controller { void disable(const string &flabel, bool disable_all_dell = true); void disable_all(); void reload(); - void reload_added(); + void recover(); void nv_init(); - void test(fc::FanInterface &fan, bool forced, function cb); + void test(fc::FanInterface &fan, bool forced, bool blocking, + shared_ptr> test_status); size_t tests_running() const; void set_devices(const fc_pb::Devices &devices_); - void from(const fc_pb::Controller &c); void from(const fc_pb::ControllerConfig &c); void to(fc_pb::Controller &c) const; void to(fc_pb::ControllerConfig &c) const; @@ -69,7 +68,11 @@ class Controller { void enable_dell_fans(); void disable_dell_fans(); - void load_conf_and_enumerated(); + bool is_testing(const string &flabel) const; + optional read_config(); + void merge(Devices &old_it, bool replace_on_match, bool deep_cmp = false); + void remove_devices_not_in( + std::initializer_list> list_of_devices); void to_file(bool backup); void update_config_write_time(); bool config_file_modified() const; diff --git a/src/Devices.cpp b/src/Devices.cpp index 89f875a..e3df2d9 100644 --- a/src/Devices.cpp +++ b/src/Devices.cpp @@ -76,6 +76,8 @@ void fc::SensorChips::enumerate(FanMap &fans, SensorMap &sensors) { } } +fc::Devices::Devices(const fc_pb::Devices &d) { from(d); } + fc::Devices::Devices(bool enumerate, bool dry_run) { if (!enumerate) return; @@ -194,6 +196,31 @@ void fc::Devices::to(fc_pb::Devices &d) const { s->to(*d.mutable_sensor()->Add()); } +// vector fc::Devices::diff(const Devices &d) const { +// vector res; +// const auto differences = [&](const auto &a, const auto &b) { +// const auto search_dif = [&](const auto &a, const auto &b) { +// for (const auto &[key, dev] : a) { +// string hw_id = dev->hw_id(); +// auto it = find_if(b.begin(), b.end(), [&](const auto &p) { +// return p.second->hw_id() == hw_id; +// }); +// +// if (it != b.end()) +// res.emplace_back(move(hw_id)); +// } +// }; +// +// search_dif(a, b); +// search_dif(b, a); +// }; +// +// differences(fans, d.fans); +// differences(sensors, d.sensors); +// +// return res; +//} + bool fc::operator==(const fc_pb::Fan &l, const fc_pb::Fan &r) { return l.type() == r.type() && l.pwm_path() == r.pwm_path() && l.rpm_path() == r.rpm_path() && l.id() == r.id(); diff --git a/src/Devices.hpp b/src/Devices.hpp index 8908c08..285f74e 100644 --- a/src/Devices.hpp +++ b/src/Devices.hpp @@ -31,6 +31,7 @@ class SensorChips { class Devices { public: Devices() = default; + Devices(const fc_pb::Devices &d); explicit Devices(bool enumerate, bool dry_run = false); FanMap fans; @@ -38,6 +39,7 @@ class Devices { void from(const fc_pb::Devices &d); void to(fc_pb::Devices &d) const; + // vector diff(const Devices &d) const; }; bool operator==(const fc_pb::Fan &l, const fc_pb::Fan &r); diff --git a/src/FanInterface.cpp b/src/FanInterface.cpp index aa62636..a1d43db 100644 --- a/src/FanInterface.cpp +++ b/src/FanInterface.cpp @@ -147,7 +147,7 @@ void fc::FanInterface::sleep_for_interval() const { sleep_for((interval.count() > 0) ? interval : fc::update_interval); } -void fc::FanInterface::test(Observable &status) { +void fc::FanInterface::test(ObservableNumber &status) { const Pwm pre_pwm = get_pwm(); // Fail early if can't write enable mode or pwm @@ -160,6 +160,7 @@ void fc::FanInterface::test(Observable &status) { } // 100 (%) should be added to status over the series of tests + status = 0; Pwm_to_Rpm_Map pwm_to_rpm; test_stopped(pwm_to_rpm); @@ -448,6 +449,13 @@ void fc::FanInterface::to(fc_pb::Fan &f) const { f.set_ignore(ignore); } +bool fc::FanInterface::deep_equal(const FanInterface &other) const { + fc_pb::Fan f, fother; + to(f); + other.to(fother); + return Util::deep_equal(f, fother); +} + std::ostream &fc::operator<<(std::ostream &os, const fc::FanInterface &f) { os << f.label; return os; diff --git a/src/FanInterface.hpp b/src/FanInterface.hpp index a80c263..1a69ebc 100644 --- a/src/FanInterface.hpp +++ b/src/FanInterface.hpp @@ -8,7 +8,7 @@ #include "Util.hpp" #include "proto/DevicesSpec.pb.h" -using fc::Util::Observable; +using fc::Util::ObservableNumber; using fc_pb::DevType; using std::abs; using std::min; @@ -46,7 +46,7 @@ class FanInterface { bool ignore{false}; void update(); - virtual void test(Observable &status); + virtual void test(ObservableNumber &status); bool tested() const; bool pre_start_check() const; @@ -61,6 +61,7 @@ class FanInterface { virtual void from(const fc_pb::Fan &f, const SensorMap &sensor_map); virtual void to(fc_pb::Fan &f) const = 0; + bool deep_equal(const FanInterface &other) const; friend std::ostream &operator<<(std::ostream &os, const FanInterface &f); protected: diff --git a/src/FanSysfs.cpp b/src/FanSysfs.cpp index 3bc4ae7..f3186bf 100644 --- a/src/FanSysfs.cpp +++ b/src/FanSysfs.cpp @@ -29,7 +29,7 @@ bool fc::FanSysfs::disable_control() const { return true; } -void fc::FanSysfs::test(Observable &status) { +void fc::FanSysfs::test(ObservableNumber &status) { test_driver_enable_flag(); fc::FanInterface::test(status); } diff --git a/src/FanSysfs.hpp b/src/FanSysfs.hpp index 34f1084..b052615 100644 --- a/src/FanSysfs.hpp +++ b/src/FanSysfs.hpp @@ -14,7 +14,7 @@ class FanSysfs : public FanInterface { FanSysfs(string label_, const path &adapter_path_, int id_); ~FanSysfs() override; - void test(Observable &status) override; + void test(ObservableNumber &status) override; bool enable_control() const override; bool disable_control() const override; Pwm get_pwm() const override; diff --git a/src/FanTask.cpp b/src/FanTask.cpp new file mode 100644 index 0000000..0ebe7d3 --- /dev/null +++ b/src/FanTask.cpp @@ -0,0 +1,27 @@ +#include "FanTask.hpp" + +fc::FanTask::FanTask(function f) + : t(thread([this](auto f) { f(run); }, move(f))) {} + +fc::FanTask::FanTask(function f, + shared_ptr> testing_status) + : test_status(move(testing_status)), t(thread(move(f))) {} + +fc::FanTask::~FanTask() { join(); } + +bool fc::FanTask::is_testing() const { return bool(test_status); } + +void fc::FanTask::join() { + // End gracefully + run = false; + if (t.joinable()) { + t.interrupt(); + t.join(); + } +} + +fc::FanTask &fc::FanTask::operator=(fc::FanTask &&other) noexcept { + t = move(other.t); + test_status = other.test_status; + return *this; +} diff --git a/src/FanTask.hpp b/src/FanTask.hpp new file mode 100644 index 0000000..3640486 --- /dev/null +++ b/src/FanTask.hpp @@ -0,0 +1,31 @@ +#ifndef FANCON_SRC_FANTHREAD_HPP +#define FANCON_SRC_FANTHREAD_HPP + +#include "Util.hpp" +#include "boost/thread.hpp" + +using boost::thread; +using fc::Util::ObservableNumber; + +namespace fc { +class FanTask { +public: + explicit FanTask(function f); + explicit FanTask(function f, + shared_ptr> testing_status); + ~FanTask(); + + shared_ptr> test_status = nullptr; + + bool is_testing() const; + void join(); + + FanTask &operator=(FanTask &&other) noexcept; + +private: + bool run = true; + thread t; +}; +} // namespace fc + +#endif // FANCON_SRC_FANTHREAD_HPP diff --git a/src/FanThread.cpp b/src/FanThread.cpp deleted file mode 100644 index cf3220f..0000000 --- a/src/FanThread.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "FanThread.hpp" - -fc::FanThread::FanThread(function f) - : t(thread([this](auto f) { f(run); }, move(f))) {} - -fc::FanThread::FanThread(function f, - shared_ptr> testing_status) - : t(thread(move(f))), test_status(move(testing_status)) {} - -fc::FanThread::~FanThread() { - // End gracefully - run = false; - join(); -} - -bool fc::FanThread::is_testing() const { return bool(test_status); } - -void fc::FanThread::join() { - if (t.joinable()) { - t.interrupt(); - t.join(); - } -} - -fc::FanThread &fc::FanThread::operator=(fc::FanThread &&other) noexcept { - t = move(other.t); - test_status = other.test_status; - return *this; -} diff --git a/src/FanThread.hpp b/src/FanThread.hpp deleted file mode 100644 index de82e62..0000000 --- a/src/FanThread.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef FANCON_SRC_FANTHREAD_HPP -#define FANCON_SRC_FANTHREAD_HPP - -#include "Util.hpp" -#include "boost/thread.hpp" - -using boost::thread; -using fc::Util::Observable; - -namespace fc { -class FanThread { -public: - explicit FanThread(function f); - explicit FanThread(function f, - shared_ptr> testing_status); - ~FanThread(); - - bool run = true; - thread t; - shared_ptr> test_status = nullptr; - - bool is_testing() const; - void join(); - FanThread &operator=(FanThread &&other) noexcept; -}; -} // namespace fc - -#endif // FANCON_SRC_FANTHREAD_HPP diff --git a/src/SensorInterface.cpp b/src/SensorInterface.cpp index b81ef45..d2f79af 100644 --- a/src/SensorInterface.cpp +++ b/src/SensorInterface.cpp @@ -28,6 +28,13 @@ void fc::SensorInterface::from(const fc_pb::Sensor &s) { label = s.label(); } void fc::SensorInterface::to(fc_pb::Sensor &s) const { s.set_label(label); } +bool fc::SensorInterface::deep_equal(const SensorInterface &other) const { + fc_pb::Sensor s, sother; + to(s); + other.to(sother); + return Util::deep_equal(s, sother); +} + bool fc::SensorInterface::fresh() const { const auto dur = chrono::duration_cast( chrono::high_resolution_clock::now() - last_read_time); diff --git a/src/SensorInterface.hpp b/src/SensorInterface.hpp index fe0c8ce..4fbe00d 100644 --- a/src/SensorInterface.hpp +++ b/src/SensorInterface.hpp @@ -30,6 +30,7 @@ class SensorInterface { virtual bool valid() const = 0; virtual string hw_id() const = 0; + bool deep_equal(const SensorInterface &other) const; friend std::ostream &operator<<(std::ostream &os, const SensorInterface &s); protected: diff --git a/src/Service.cpp b/src/Service.cpp index ba062b1..7167425 100644 --- a/src/Service.cpp +++ b/src/Service.cpp @@ -32,7 +32,6 @@ void fc::Service::run() { } void fc::Service::shutdown() { - controller.disable_all(); if (server) server->Shutdown(Util::deadline(250)); } @@ -209,7 +208,8 @@ Status fc::Service::Test([[maybe_unused]] ServerContext *context, writer->Write(resp); }; - controller.test(*it->second, e->forced(), cb); + controller.test(*it->second, e->forced(), true, + make_shared>(cb)); return Status::OK; } @@ -221,6 +221,13 @@ Status fc::Service::Reload([[maybe_unused]] ServerContext *context, return Status::OK; } +Status fc::Service::Recover([[maybe_unused]] ServerContext *context, + [[maybe_unused]] const fc_pb::Empty *e, + [[maybe_unused]] fc_pb::Empty *resp) { + controller.recover(); + return Status::OK; +} + Status fc::Service::NvInit([[maybe_unused]] ServerContext *context, [[maybe_unused]] const fc_pb::Empty *e, [[maybe_unused]] fc_pb::Empty *resp) { diff --git a/src/Service.hpp b/src/Service.hpp index 19dfd74..475439a 100644 --- a/src/Service.hpp +++ b/src/Service.hpp @@ -68,6 +68,8 @@ class Service : public fc_pb::DService::Service { ServerWriter *writer) override; Status Reload(ServerContext *context, const fc_pb::Empty *e, fc_pb::Empty *resp) override; + Status Recover(ServerContext *context, const fc_pb::Empty *e, + fc_pb::Empty *resp) override; Status NvInit(ServerContext *context, const fc_pb::Empty *e, fc_pb::Empty *resp) override; diff --git a/src/Util.cpp b/src/Util.cpp index 217a902..5d748d0 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -57,6 +57,12 @@ std::chrono::high_resolution_clock::time_point fc::Util::deadline(long ms) { std::chrono::milliseconds(ms); } +bool fc::Util::deep_equal(const google::protobuf::Message &m1, + const google::protobuf::Message &m2) { + return m1.ByteSizeLong() == m2.ByteSizeLong() && + m1.SerializeAsString() == m2.SerializeAsString(); +} + fc::Util::ScopedCounter fc::Util::RemovableMutex::acquire_lock() { while (counter < 0) sleep_for(milliseconds(50)); diff --git a/src/Util.hpp b/src/Util.hpp index 3fbecbe..082777f 100644 --- a/src/Util.hpp +++ b/src/Util.hpp @@ -19,6 +19,7 @@ //#include #include #include +#include #include #include @@ -74,20 +75,26 @@ string join(std::initializer_list> args, bool is_root(); bool is_atty(); std::chrono::high_resolution_clock::time_point deadline(long ms); +bool deep_equal(const google::protobuf::Message &m1, + const google::protobuf::Message &m2); -template class Observable { +template class ObservableNumber { public: - explicit Observable(T &&value) : value(value) {} + explicit ObservableNumber(T &&value) : value(value) {} + ObservableNumber(function f, T &&value = 0) : value(value) { + register_observer(f); + } + + vector> observers; void register_observer(function callback); void notify_observers(); - Observable &operator=(T other); - Observable &operator+=(const T &other); + ObservableNumber &operator=(T other); + ObservableNumber &operator+=(const T &other); private: T value; - vector> observers; mutex update_mutex; }; @@ -205,19 +212,20 @@ string fc::Util::map_str(const std::map m) { } template -void fc::Util::Observable::register_observer( +void fc::Util::ObservableNumber::register_observer( std::function callback) { callback(value); observers.emplace_back(move(callback)); } -template void fc::Util::Observable::notify_observers() { +template void fc::Util::ObservableNumber::notify_observers() { for (auto &f : observers) f(value); } template -fc::Util::Observable &fc::Util::Observable::operator+=(const T &other) { +fc::Util::ObservableNumber & +fc::Util::ObservableNumber::operator+=(const T &other) { const lock_guard lock(update_mutex); value += other; notify_observers(); @@ -225,7 +233,8 @@ fc::Util::Observable &fc::Util::Observable::operator+=(const T &other) { } template -fc::Util::Observable &fc::Util::Observable::operator=(T other) { +fc::Util::ObservableNumber & +fc::Util::ObservableNumber::operator=(T other) { const lock_guard lock(update_mutex); value = move(other); notify_observers(); diff --git a/src/main.cpp b/src/main.cpp index b3a4daa..027940e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,9 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } + if (!is_systemd()) + LOG(llvl::info) << "Service started"; + fc::Service service(config_path, args.daemon); register_signal_handler(); service.run();