fix(mango): reconnect IPC event thread on disconnect
The IPC event thread had no reconnect: on POLLHUP/POLLERR/POLLNVAL or read()==0/error it broke out of the loop and the thread exited permanently, freezing every mango module with stale content until Waybar was restarted. Wrap the connect + poll/read loop in a reconnect loop with a bounded 2s backoff, re-establishing the socket and resuming on disconnect, modeled on the niri backend. Add an atomic running_ flag so the worker exits cleanly on teardown; the destructor now sets it false before closing the socket so the worker breaks out and joins, and leaves the final close to the destructor to avoid a double close.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
// include/modules/mango/backend.hpp
|
// include/modules/mango/backend.hpp
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -52,6 +53,7 @@ class IPC {
|
|||||||
|
|
||||||
static Json::Value sendCommand(const std::string& cmd);
|
static Json::Value sendCommand(const std::string& cmd);
|
||||||
|
|
||||||
|
std::atomic<bool> running_ = true;
|
||||||
int sockfd_ = -1;
|
int sockfd_ = -1;
|
||||||
std::thread ipc_thread_;
|
std::thread ipc_thread_;
|
||||||
mutable std::mutex data_mutex_;
|
mutable std::mutex data_mutex_;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -100,42 +101,77 @@ void IPC::sendAsync(const Json::Value& request) {
|
|||||||
IPC::IPC() : sockfd_(-1), active_client_(Json::nullValue) { startIPC(); }
|
IPC::IPC() : sockfd_(-1), active_client_(Json::nullValue) { startIPC(); }
|
||||||
|
|
||||||
IPC::~IPC() {
|
IPC::~IPC() {
|
||||||
|
running_ = false;
|
||||||
if (sockfd_ != -1) close(sockfd_);
|
if (sockfd_ != -1) close(sockfd_);
|
||||||
if (ipc_thread_.joinable()) ipc_thread_.join();
|
if (ipc_thread_.joinable()) ipc_thread_.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IPC::startIPC() {
|
void IPC::startIPC() {
|
||||||
|
// Connect synchronously so a missing socket (this WM isn't the active
|
||||||
|
// compositor) throws here and lets the module constructor fail, instead of
|
||||||
|
// the module always attaching with a permanently empty widget.
|
||||||
sockfd_ = IPC::connectToSocket();
|
sockfd_ = IPC::connectToSocket();
|
||||||
|
|
||||||
ipc_thread_ = std::thread([this]() {
|
ipc_thread_ = std::thread([this]() {
|
||||||
spdlog::info("Mango IPC thread started");
|
spdlog::info("Mango IPC thread started");
|
||||||
|
|
||||||
struct pollfd pfd;
|
char buf[4096];
|
||||||
pfd.fd = sockfd_;
|
std::string buffer;
|
||||||
pfd.events = POLLIN;
|
bool have_initial_fd = true;
|
||||||
|
|
||||||
|
// Reconnect loop: if the event stream drops (POLLHUP/POLLERR, read()==0 or
|
||||||
|
// an error) we back off briefly and re-establish the socket instead of
|
||||||
|
// leaving every mango module frozen forever with stale content.
|
||||||
|
while (running_) {
|
||||||
|
if (!have_initial_fd) {
|
||||||
|
try {
|
||||||
|
sockfd_ = IPC::connectToSocket();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
spdlog::error("Mango IPC: failed to reconnect: {}", e.what());
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
have_initial_fd = false;
|
||||||
|
|
||||||
|
bool subscribed = true;
|
||||||
const std::vector<std::string> subs = {"watch all-monitors"};
|
const std::vector<std::string> subs = {"watch all-monitors"};
|
||||||
for (const auto& cmd : subs) {
|
for (const auto& cmd : subs) {
|
||||||
if (write(sockfd_, cmd.c_str(), cmd.size()) != (ssize_t)cmd.size() ||
|
if (write(sockfd_, cmd.c_str(), cmd.size()) != (ssize_t)cmd.size() ||
|
||||||
write(sockfd_, "\n", 1) != 1) {
|
write(sockfd_, "\n", 1) != 1) {
|
||||||
spdlog::error("Failed to subscribe to {}", cmd);
|
spdlog::error("Failed to subscribe to {}", cmd);
|
||||||
return;
|
subscribed = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!subscribed) {
|
||||||
|
if (sockfd_ != -1) {
|
||||||
|
close(sockfd_);
|
||||||
|
sockfd_ = -1;
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
char buf[4096];
|
struct pollfd pfd;
|
||||||
std::string buffer;
|
pfd.fd = sockfd_;
|
||||||
while (true) {
|
pfd.events = POLLIN;
|
||||||
|
buffer.clear();
|
||||||
|
|
||||||
|
bool connected = true;
|
||||||
|
while (running_ && connected) {
|
||||||
int ret = poll(&pfd, 1, 1000);
|
int ret = poll(&pfd, 1, 1000);
|
||||||
if (ret == 0) continue;
|
if (ret == 0) continue;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno == EINTR) continue;
|
if (errno == EINTR) continue;
|
||||||
spdlog::error("IPC poll error: {}", strerror(errno));
|
spdlog::error("IPC poll error: {}", strerror(errno));
|
||||||
|
connected = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
|
if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
|
||||||
spdlog::info("Mango IPC socket closed or invalid");
|
spdlog::info("Mango IPC socket closed or invalid");
|
||||||
|
connected = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,11 +179,13 @@ void IPC::startIPC() {
|
|||||||
ssize_t n = read(sockfd_, buf, sizeof(buf));
|
ssize_t n = read(sockfd_, buf, sizeof(buf));
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
spdlog::info("Mango IPC connection closed");
|
spdlog::info("Mango IPC connection closed");
|
||||||
|
connected = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
if (errno == EINTR) continue;
|
if (errno == EINTR) continue;
|
||||||
spdlog::error("IPC read error: {}", strerror(errno));
|
spdlog::error("IPC read error: {}", strerror(errno));
|
||||||
|
connected = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
buffer.append(buf, n);
|
buffer.append(buf, n);
|
||||||
@@ -165,6 +203,19 @@ void IPC::startIPC() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On shutdown leave the socket for the destructor to close (avoids a
|
||||||
|
// double close); on a genuine disconnect close it before reconnecting.
|
||||||
|
if (!running_) break;
|
||||||
|
if (sockfd_ != -1) {
|
||||||
|
close(sockfd_);
|
||||||
|
sockfd_ = -1;
|
||||||
|
}
|
||||||
|
spdlog::warn("Mango IPC: event stream closed, reconnecting");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog::info("Mango IPC thread stopping");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user