Separate worker thread into util class

pull/1276/head
Risto Toijala 2021-10-03 13:17:51 +03:00
parent 6eb9606f23
commit ccdc9b8e0b
3 changed files with 157 additions and 89 deletions

View File

@ -1,27 +1,28 @@
#pragma once
#include <fmt/format.h>
#include <csignal>
#include <string>
#include "ALabel.hpp"
#include "util/command.hpp"
#include "util/json.hpp"
#include "util/sleeper_thread.hpp"
#include "util/worker_thread.hpp"
namespace waybar::modules {
class Custom : public ALabel {
public:
Custom(const std::string&, const std::string&, const Json::Value&);
~Custom();
auto update() -> void;
void refresh(int /*signal*/);
private:
void delayWorker();
void continuousWorker();
void parseOutputRaw();
void parseOutputJson();
void workerExitCallback(int);
void workerOutputCallback(std::string);
void parseOutputRaw(const std::string&);
void parseOutputJson(const std::string&);
void handleEvent();
bool handleScroll(GdkEventScroll* e);
bool handleToggle(GdkEventButton* const& e);
@ -32,12 +33,10 @@ class Custom : public ALabel {
std::string tooltip_;
std::vector<std::string> class_;
int percentage_;
FILE* fp_;
int pid_;
util::command::res output_;
util::JsonParser parser_;
util::SleeperThread thread_;
waybar::util::WorkerThread thread_;
};
} // namespace waybar::modules

View File

@ -0,0 +1,132 @@
#pragma once
#include <chrono>
#include <optional>
#include <string>
#include "util/command.hpp"
#include "util/sleeper_thread.hpp"
namespace waybar::util {
/**
* Runs a child process and reads output from it, providing it to
* the callbacks.
*/
class WorkerThread {
public:
WorkerThread() : pid_(-1) {}
WorkerThread(const Json::Value& config, std::function<void(std::string)> output_callback,
std::function<void(int)> exit_callback)
: output_callback_(output_callback), exit_callback_(exit_callback), pid_(-1) {
if (config["exec"].isString()) {
exec_ = config["exec"].asString();
}
if (config["exec_if"].isString()) {
exec_if_ = config["exec_if"].asString();
}
if (config["interval"].isUInt()) {
interval_ = std::chrono::seconds(config["interval"].asUInt());
} else if (config["interval"] == "once") {
interval_ = std::chrono::seconds(100000000);
} else {
interval_ = std::chrono::seconds(0);
}
if (config["restart-interval"].isUInt()) {
restart_interval_ = std::chrono::seconds(config["restart-interval"].asUInt());
}
if (interval_.count() > 0) {
thread_ = [this] { delay_worker(); };
} else if (!exec_.empty()) {
thread_ = [this] { continuous_worker(); };
}
}
~WorkerThread() {
if (pid_ != -1) {
killpg(pid_, SIGTERM);
pid_ = -1;
}
}
auto wake_up() { return thread_.wake_up(); }
private:
void delay_worker() {
bool can_update = true;
if (!exec_if_.empty()) {
util::command::res output = util::command::execNoRead(exec_if_);
if (output.exit_code != 0) {
can_update = false;
exit_callback_(output.exit_code);
}
}
if (can_update) {
if (!exec_.empty()) {
util::command::res output = util::command::exec(exec_);
if (output.exit_code == 0) {
output_callback_(std::move(output.out));
} else {
exit_callback_(output.exit_code);
}
}
}
thread_.sleep_for(interval_);
}
void continuous_worker() {
pid_ = -1;
FILE* fp = util::command::open(exec_, pid_);
if (!fp) {
throw std::runtime_error("Unable to open " + exec_);
}
while (true) {
char* buff = nullptr;
size_t len = 0;
if (getline(&buff, &len, fp) == -1) {
int exit_code = 1;
if (fp) {
exit_code = WEXITSTATUS(util::command::close(fp, pid_));
fp = nullptr;
}
if (exit_code != 0) {
spdlog::error("'{}' stopped unexpectedly, is it endless?", exec_);
exit_callback_(exit_code);
}
if (restart_interval_.has_value()) {
pid_ = -1;
thread_.sleep_for(std::chrono::seconds(*restart_interval_));
fp = util::command::open(exec_, pid_);
if (!fp) {
throw std::runtime_error("Unable to open " + exec_);
}
} else {
thread_.stop();
return;
}
} else {
std::string output = buff;
// Remove last newline
if (!output.empty() && output[output.length() - 1] == '\n') {
output.erase(output.length() - 1);
}
output_callback_(std::move(output));
}
}
}
std::string exec_;
std::string exec_if_;
std::chrono::seconds interval_;
std::optional<std::chrono::seconds> restart_interval_;
std::function<void(std::string)> output_callback_;
std::function<void(int)> exit_callback_;
int pid_;
SleeperThread thread_;
};
} // namespace waybar::util

View File

@ -4,85 +4,22 @@
waybar::modules::Custom::Custom(const std::string& name, const std::string& id,
const Json::Value& config)
: ALabel(config, "custom-" + name, id, "{}"), name_(name), fp_(nullptr), pid_(-1) {
: ALabel(config, "custom-" + name, id, "{}"),
name_(name),
thread_(
config, [this](std::string output) { workerOutputCallback(std::move(output)); },
[this](int exit_code) { workerExitCallback(exit_code); }) {
dp.emit();
if (interval_.count() > 0) {
delayWorker();
} else if (config_["exec"].isString()) {
continuousWorker();
}
}
waybar::modules::Custom::~Custom() {
if (pid_ != -1) {
killpg(pid_, SIGTERM);
pid_ = -1;
}
}
void waybar::modules::Custom::delayWorker() {
thread_ = [this] {
bool can_update = true;
if (config_["exec-if"].isString()) {
output_ = util::command::execNoRead(config_["exec-if"].asString());
if (output_.exit_code != 0) {
can_update = false;
dp.emit();
}
}
if (can_update) {
if (config_["exec"].isString()) {
output_ = util::command::exec(config_["exec"].asString());
}
dp.emit();
}
thread_.sleep_for(interval_);
};
}
void waybar::modules::Custom::continuousWorker() {
auto cmd = config_["exec"].asString();
pid_ = -1;
fp_ = util::command::open(cmd, pid_);
if (!fp_) {
throw std::runtime_error("Unable to open " + cmd);
}
thread_ = [this, cmd] {
char* buff = nullptr;
size_t len = 0;
if (getline(&buff, &len, fp_) == -1) {
int exit_code = 1;
if (fp_) {
exit_code = WEXITSTATUS(util::command::close(fp_, pid_));
fp_ = nullptr;
}
if (exit_code != 0) {
void waybar::modules::Custom::workerExitCallback(int exit_code) {
output_ = {exit_code, ""};
dp.emit();
spdlog::error("{} stopped unexpectedly, is it endless?", name_);
}
if (config_["restart-interval"].isUInt()) {
pid_ = -1;
thread_.sleep_for(std::chrono::seconds(config_["restart-interval"].asUInt()));
fp_ = util::command::open(cmd, pid_);
if (!fp_) {
throw std::runtime_error("Unable to open " + cmd);
}
} else {
thread_.stop();
return;
}
} else {
std::string output = buff;
}
// Remove last newline
if (!output.empty() && output[output.length() - 1] == '\n') {
output.erase(output.length() - 1);
}
output_ = {0, output};
void waybar::modules::Custom::workerOutputCallback(std::string output) {
output_ = {0, std::move(output)};
dp.emit();
}
};
}
void waybar::modules::Custom::refresh(int sig) {
@ -116,9 +53,9 @@ auto waybar::modules::Custom::update() -> void {
event_box_.hide();
} else {
if (config_["return-type"].asString() == "json") {
parseOutputJson();
parseOutputJson(output_.out);
} else {
parseOutputRaw();
parseOutputRaw(output_.out);
}
auto str = fmt::format(format_,
text_,
@ -154,8 +91,8 @@ auto waybar::modules::Custom::update() -> void {
ALabel::update();
}
void waybar::modules::Custom::parseOutputRaw() {
std::istringstream output(output_.out);
void waybar::modules::Custom::parseOutputRaw(const std::string& output_str) {
std::istringstream output(output_str);
std::string line;
int i = 0;
while (getline(output, line)) {
@ -178,8 +115,8 @@ void waybar::modules::Custom::parseOutputRaw() {
}
}
void waybar::modules::Custom::parseOutputJson() {
std::istringstream output(output_.out);
void waybar::modules::Custom::parseOutputJson(const std::string& output_str) {
std::istringstream output(output_str);
std::string line;
class_.clear();
while (getline(output, line)) {