fix(niri): reconnect IPC and stop per-event throttling to prevent freeze

The niri IPC worker slept 1ms per event and never reconnected. Under an
event burst the per-event cap back-pressures the socket, niri fills its
send buffer and drops the stream; read_line then returns false, the
detached thread exits and the module freezes permanently.

Remove the per-event sleep so events drain as fast as they arrive, and
wrap the socket setup and read loop in a reconnect loop that backs off
and re-establishes the stream on drop. A running_ flag lets the thread
exit cleanly on teardown.

Fixes #5117.
This commit is contained in:
Alex
2026-07-04 03:14:13 +02:00
parent 3831524ba8
commit 6672e924df
2 changed files with 58 additions and 32 deletions
+4
View File
@@ -1,5 +1,6 @@
#pragma once
#include <atomic>
#include <list>
#include <mutex>
#include <string>
@@ -18,6 +19,7 @@ class EventHandler {
class IPC {
public:
IPC();
~IPC();
void registerForIPC(const std::string& ev, EventHandler* ev_handler);
void unregisterForIPC(EventHandler* handler);
@@ -45,6 +47,8 @@ class IPC {
util::JsonParser parser_;
std::mutex callbackMutex_;
std::list<std::pair<std::string, EventHandler*>> callbacks_;
std::atomic<bool> running_{true};
};
inline std::unique_ptr<IPC> gIPC;
+30 -8
View File
@@ -23,6 +23,8 @@ namespace waybar::modules::niri {
IPC::IPC() { startIPC(); }
IPC::~IPC() { running_ = false; }
int IPC::connectToSocket() {
const char* socket_path = getenv("NIRI_SOCKET");
@@ -55,11 +57,21 @@ int IPC::connectToSocket() {
void IPC::startIPC() {
// will start IPC and relay events to parseIPC
int socketfd = connectToSocket();
std::thread([this, socketfd]() {
std::thread([this]() {
spdlog::info("Niri IPC starting");
// Reconnect loop: if the event stream drops we back off briefly and
// re-establish the socket instead of leaving the module frozen forever.
while (running_) {
int socketfd;
try {
socketfd = connectToSocket();
} catch (std::exception& e) {
spdlog::error("Niri IPC: failed to connect: {}", e.what());
std::this_thread::sleep_for(std::chrono::seconds(2));
continue;
}
auto unix_istream = Gio::UnixInputStream::create(socketfd, true);
auto unix_ostream = Gio::UnixOutputStream::create(socketfd, false);
auto istream = Gio::DataInputStream::create(unix_istream);
@@ -67,16 +79,20 @@ void IPC::startIPC() {
if (!ostream->put_string("\"EventStream\"\n") || !ostream->flush()) {
spdlog::error("Niri IPC: failed to start event stream");
return;
std::this_thread::sleep_for(std::chrono::seconds(2));
continue;
}
std::string line;
if (!istream->read_line(line) || line != R"({"Ok":"Handled"})") {
spdlog::error("Niri IPC: failed to start event stream");
return;
std::this_thread::sleep_for(std::chrono::seconds(2));
continue;
}
while (istream->read_line(line)) {
// Drain events as fast as they arrive; throttling here back-pressures the
// socket, fills niri's send buffer and makes niri drop the stream.
while (running_ && istream->read_line(line)) {
spdlog::debug("Niri IPC: received {}", line);
try {
@@ -86,9 +102,15 @@ void IPC::startIPC() {
} catch (...) {
throw;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
if (!running_) break;
spdlog::warn("Niri IPC: event stream closed, reconnecting");
std::this_thread::sleep_for(std::chrono::seconds(2));
}
spdlog::info("Niri IPC stopping");
}).detach();
}