Merge pull request #278 from minijackson/mpd-timeout

fix(mpd): regularly timeout the event listener to prevent timeout
This commit is contained in:
Alex 2019-04-21 19:13:38 +02:00 committed by GitHub
commit 263a32c9af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 21 deletions

View File

@ -48,6 +48,8 @@ class MPD : public ALabel {
const char* server_; const char* server_;
const unsigned port_; const unsigned port_;
unsigned timeout_;
// We need a mutex here because we can trigger updates from multiple thread: // We need a mutex here because we can trigger updates from multiple thread:
// the event based updates, the periodic updates needed for the elapsed time, // the event based updates, the periodic updates needed for the elapsed time,
// and the click play/pause feature // and the click play/pause feature

View File

@ -7,17 +7,29 @@ waybar::modules::MPD::MPD(const std::string& id, const Json::Value& config)
: ALabel(config, "{album} - {artist} - {title}", 5), : ALabel(config, "{album} - {artist} - {title}", 5),
module_name_(id.empty() ? "mpd" : "mpd#" + id), module_name_(id.empty() ? "mpd" : "mpd#" + id),
server_(nullptr), server_(nullptr),
port_(config["port"].asUInt()), port_(config_["port"].isUInt() ? config["port"].asUInt() : 0),
timeout_(config_["timeout"].isUInt() ? config_["timeout"].asUInt() * 1'000 : 30'000),
connection_(nullptr, &mpd_connection_free), connection_(nullptr, &mpd_connection_free),
alternate_connection_(nullptr, &mpd_connection_free), alternate_connection_(nullptr, &mpd_connection_free),
status_(nullptr, &mpd_status_free), status_(nullptr, &mpd_status_free),
song_(nullptr, &mpd_song_free) { song_(nullptr, &mpd_song_free) {
if (!config_["port"].isNull() && !config_["port"].isUInt()) {
std::cerr << module_name_ << ": `port` configuration should be an unsigned int" << std::endl;
}
if (!config_["timeout"].isNull() && !config_["timeout"].isUInt()) {
std::cerr << module_name_ << ": `timeout` configuration should be an unsigned int" << std::endl;
}
label_.set_name("mpd"); label_.set_name("mpd");
if (!id.empty()) { if (!id.empty()) {
label_.get_style_context()->add_class(id); label_.get_style_context()->add_class(id);
} }
if (!config["server"].isNull()) { if (!config["server"].isNull()) {
if (!config_["server"].isString()) {
std::cerr << module_name_ << "`server` configuration should be a string" << std::endl;
}
server_ = config["server"].asCString(); server_ = config["server"].asCString();
} }
@ -50,17 +62,17 @@ auto waybar::modules::MPD::update() -> void {
std::thread waybar::modules::MPD::event_listener() { std::thread waybar::modules::MPD::event_listener() {
return std::thread([this] { return std::thread([this] {
while (true) { while (true) {
if (connection_ == nullptr) { try {
// Retry periodically if no connection if (connection_ == nullptr) {
try { // Retry periodically if no connection
update(); update();
} catch (const std::exception& e) { std::this_thread::sleep_for(interval_);
std::cerr << module_name_ + ": " + e.what() << std::endl; } else {
waitForEvent();
dp.emit();
} }
std::this_thread::sleep_for(interval_); } catch (const std::exception& e) {
} else { std::cerr << module_name_ + ": " + e.what() << std::endl;
waitForEvent();
dp.emit();
} }
} }
}); });
@ -76,12 +88,12 @@ std::thread waybar::modules::MPD::periodic_updater() {
} }
std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) { std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) {
std::string result = config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A"; std::string result =
config_["unknown-tag"].isString() ? config_["unknown-tag"].asString() : "N/A";
const char* tag = mpd_song_get_tag(song_.get(), type, idx); const char* tag = mpd_song_get_tag(song_.get(), type, idx);
// mpd_song_get_tag can return NULL, so make sure it's valid before setting // mpd_song_get_tag can return NULL, so make sure it's valid before setting
if (tag) if (tag) result = tag;
result = tag;
return result; return result;
} }
@ -89,8 +101,9 @@ std::string waybar::modules::MPD::getTag(mpd_tag_type type, unsigned idx) {
void waybar::modules::MPD::setLabel() { void waybar::modules::MPD::setLabel() {
if (connection_ == nullptr) { if (connection_ == nullptr) {
label_.get_style_context()->add_class("disconnected"); label_.get_style_context()->add_class("disconnected");
// In the case connection closed while MPD is stopped
label_.get_style_context()->remove_class("stopped"); label_.get_style_context()->remove_class("stopped");
label_.get_style_context()->remove_class("playing");
label_.get_style_context()->remove_class("paused");
auto format = config_["format-disconnected"].isString() auto format = config_["format-disconnected"].isString()
? config_["format-disconnected"].asString() ? config_["format-disconnected"].asString()
@ -231,10 +244,11 @@ void waybar::modules::MPD::tryConnect() {
return; return;
} }
connection_ = unique_connection(mpd_connection_new(server_, port_, 5'000), &mpd_connection_free); connection_ =
unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
alternate_connection_ = alternate_connection_ =
unique_connection(mpd_connection_new(server_, port_, 5'000), &mpd_connection_free); unique_connection(mpd_connection_new(server_, port_, timeout_), &mpd_connection_free);
if (connection_ == nullptr || alternate_connection_ == nullptr) { if (connection_ == nullptr || alternate_connection_ == nullptr) {
std::cerr << module_name_ << ": Failed to connect to MPD" << std::endl; std::cerr << module_name_ << ": Failed to connect to MPD" << std::endl;
@ -245,24 +259,27 @@ void waybar::modules::MPD::tryConnect() {
try { try {
checkErrors(connection_.get()); checkErrors(connection_.get());
} catch (std::runtime_error &e) { } catch (std::runtime_error& e) {
std::cerr << module_name_ << ": Failed to connect to MPD: " << e.what() << std::endl; std::cerr << module_name_ << ": Failed to connect to MPD: " << e.what() << std::endl;
connection_.reset(); connection_.reset();
alternate_connection_.reset(); alternate_connection_.reset();
} }
std::cerr << module_name_ << ": Connected to MPD" << std::endl;
} }
void waybar::modules::MPD::checkErrors(mpd_connection* conn) { void waybar::modules::MPD::checkErrors(mpd_connection* conn) {
switch (mpd_connection_get_error(conn)) { switch (mpd_connection_get_error(conn)) {
case MPD_ERROR_SUCCESS: case MPD_ERROR_SUCCESS:
mpd_connection_clear_error(conn);
return; return;
case MPD_ERROR_TIMEOUT:
case MPD_ERROR_CLOSED: case MPD_ERROR_CLOSED:
std::cerr << module_name_ << ": Connection to MPD closed" << std::endl;
mpd_connection_clear_error(conn); mpd_connection_clear_error(conn);
connection_.reset(); connection_.reset();
alternate_connection_.reset(); alternate_connection_.reset();
state_ = MPD_STATE_UNKNOWN; state_ = MPD_STATE_UNKNOWN;
return; throw std::runtime_error("Connection to MPD closed");
default: default:
auto error_message = mpd_connection_get_error_message(conn); auto error_message = mpd_connection_get_error_message(conn);
mpd_connection_clear_error(conn); mpd_connection_clear_error(conn);
@ -285,8 +302,19 @@ void waybar::modules::MPD::waitForEvent() {
auto conn = alternate_connection_.get(); auto conn = alternate_connection_.get();
// Wait for a player (play/pause), option (random, shuffle, etc.), or playlist // Wait for a player (play/pause), option (random, shuffle, etc.), or playlist
// change // change
mpd_run_idle_mask(conn, if (!mpd_send_idle_mask(
static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_PLAYLIST)); conn, static_cast<mpd_idle>(MPD_IDLE_PLAYER | MPD_IDLE_OPTIONS | MPD_IDLE_PLAYLIST))) {
checkErrors(conn);
return;
}
// alternate_idle_ = true;
// See issue #277:
// https://github.com/Alexays/Waybar/issues/277
mpd_recv_idle(conn, /* disable_timeout = */ false);
checkErrors(conn);
mpd_response_finish(conn);
checkErrors(conn); checkErrors(conn);
} }