From 249c3ec141daac1dc2a4c683cbe7fa9d8b7f340f Mon Sep 17 00:00:00 2001
From: patrick96
Date: Wed, 17 Feb 2021 19:48:46 +0100
Subject: [PATCH] Use libuv for the controller's event loop
---
cmake/libpoly.cmake | 2 +
cmake/modules/FindLibUV.cmake | 13 ++
include/components/controller.hpp | 5 +-
include/modules/meta/base.hpp | 13 ++
src/CMakeLists.txt | 1 +
src/components/controller.cpp | 216 ++++++++++++------------------
src/modules/xwindow.cpp | 6 +-
7 files changed, 118 insertions(+), 138 deletions(-)
create mode 100644 cmake/modules/FindLibUV.cmake
diff --git a/cmake/libpoly.cmake b/cmake/libpoly.cmake
index b465919b..f30662a4 100644
--- a/cmake/libpoly.cmake
+++ b/cmake/libpoly.cmake
@@ -98,6 +98,8 @@ endif()
find_package(Xcb ${XRANDR_VERSION} REQUIRED COMPONENTS RANDR)
find_package(Xcb REQUIRED COMPONENTS ${XORG_EXTENSIONS})
+find_package(LibUV 1.10.0 REQUIRED)
+
# FreeBSD Support
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
find_package(LibInotify REQUIRED)
diff --git a/cmake/modules/FindLibUV.cmake b/cmake/modules/FindLibUV.cmake
new file mode 100644
index 00000000..a9356ae8
--- /dev/null
+++ b/cmake/modules/FindLibUV.cmake
@@ -0,0 +1,13 @@
+# This module defines
+# LIBUV_FOUND
+# LIBUV_INCLUDE_DIR
+# LIBUV_INCLUDE_DIRS
+# LIBUV_LIBRARY
+# LIBUV_LIBRARIES
+# LIBUV_VERSION
+
+find_package_impl("libuv" "LIBUV" "")
+
+if(LIBUV_FOUND AND NOT TARGET LibUV::LibUV)
+ create_imported_target("LibUV::LibUV" "${LIBUV_INCLUDE_DIR}" "${LIBUV_LIBRARY}")
+endif()
diff --git a/include/components/controller.hpp b/include/components/controller.hpp
index a11eea02..e8d84c5b 100644
--- a/include/components/controller.hpp
+++ b/include/components/controller.hpp
@@ -52,6 +52,9 @@ class controller
bool enqueue(event&& evt);
bool enqueue(string&& input_data);
+ void conn_cb(int status, int events);
+ void ipc_cb(int status, int events);
+
protected:
void read_events();
void process_eventqueue();
@@ -84,8 +87,6 @@ class controller
unique_ptr m_ipc;
unique_ptr m_confwatch;
- array, 2> m_queuefd{};
-
/**
* \brief State flag
*/
diff --git a/include/modules/meta/base.hpp b/include/modules/meta/base.hpp
index 84722aa5..9e40f2b1 100644
--- a/include/modules/meta/base.hpp
+++ b/include/modules/meta/base.hpp
@@ -169,6 +169,19 @@ namespace modules {
void sleep(chrono::duration duration);
template
void sleep_until(chrono::time_point point);
+
+ /**
+ * Wakes up the module.
+ *
+ * It should be possible to interrupt any blocking operation inside a
+ * module using this function.
+ *
+ * In addition, after a wake up whatever was woken up should immediately
+ * check whether the module is still running.
+ *
+ * Modules that don't follow this, could stall the operation of whatever
+ * code called this function.
+ */
void wakeup();
string get_format() const;
string get_output();
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d4022142..3200d3a4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -151,6 +151,7 @@ if(BUILD_LIBPOLY)
Cairo::CairoFC
moodycamel
xpp
+ LibUV::LibUV
)
if (TARGET i3ipc++)
diff --git a/src/components/controller.cpp b/src/components/controller.cpp
index 8d11e331..d9baebac 100644
--- a/src/components/controller.cpp
+++ b/src/components/controller.cpp
@@ -1,5 +1,7 @@
#include "components/controller.hpp"
+#include
+
#include
#include
@@ -25,22 +27,9 @@
POLYBAR_NS
-array g_eventpipe{{-1, -1}};
sig_atomic_t g_reload{0};
sig_atomic_t g_terminate{0};
-void interrupt_handler(int signum) {
- if (g_reload || g_terminate) {
- return;
- }
-
- g_terminate = 1;
- g_reload = (signum == SIGUSR1);
- if (write(g_eventpipe[PIPE_WRITE], &g_terminate, 1) == -1) {
- throw system_error("Failed to write to eventpipe");
- }
-}
-
/**
* Build controller instance
*/
@@ -70,23 +59,6 @@ controller::controller(connection& conn, signal_emitter& emitter, const logger&
m_swallow_limit = m_conf.deprecated("settings", "eventqueue-swallow", "throttle-output", m_swallow_limit);
m_swallow_update = m_conf.deprecated("settings", "eventqueue-swallow-time", "throttle-output-for", m_swallow_update);
- if (pipe(g_eventpipe.data()) == 0) {
- m_queuefd[PIPE_READ] = make_unique(g_eventpipe[PIPE_READ]);
- m_queuefd[PIPE_WRITE] = make_unique(g_eventpipe[PIPE_WRITE]);
- } else {
- throw system_error("Failed to create event channel pipes");
- }
-
- m_log.trace("controller: Install signal handler");
- struct sigaction act {};
- memset(&act, 0, sizeof(act));
- act.sa_handler = &interrupt_handler;
- sigaction(SIGINT, &act, nullptr);
- sigaction(SIGQUIT, &act, nullptr);
- sigaction(SIGTERM, &act, nullptr);
- sigaction(SIGUSR1, &act, nullptr);
- sigaction(SIGALRM, &act, nullptr);
-
m_log.trace("controller: Setup user-defined modules");
size_t created_modules{0};
created_modules += setup_modules(alignment::LEFT);
@@ -102,17 +74,6 @@ controller::controller(connection& conn, signal_emitter& emitter, const logger&
* Deconstruct controller
*/
controller::~controller() {
- m_log.trace("controller: Uninstall sighandler");
- signal(SIGINT, SIG_DFL);
- signal(SIGQUIT, SIG_DFL);
- signal(SIGTERM, SIG_DFL);
- signal(SIGALRM, SIG_DFL);
-
- if (g_reload) {
- // Cause SIGUSR1 to be ignored until registered in the new polybar process
- signal(SIGUSR1, SIG_IGN);
- }
-
m_log.trace("controller: Detach signal receiver");
m_sig.detach(this);
@@ -211,112 +172,101 @@ bool controller::enqueue(string&& input_data) {
return false;
}
+void controller::conn_cb(int status, int events) {
+ // TODO handle negative status
+
+ if (m_connection.connection_has_error()) {
+ g_terminate = 1;
+ g_reload = 0;
+ uv_stop(uv_default_loop());
+ return;
+ }
+
+ shared_ptr evt{};
+ while ((evt = shared_ptr(xcb_poll_for_event(m_connection), free)) != nullptr) {
+ try {
+ m_connection.dispatch_event(evt);
+ } catch (xpp::connection_error& err) {
+ m_log.err("X connection error, terminating... (what: %s)", m_connection.error_str(err.code()));
+ } catch (const exception& err) {
+ m_log.err("Error in X event loop: %s", err.what());
+ }
+ }
+}
+
+void controller::ipc_cb(int status, int events) {
+ // TODO handle negative status
+ m_ipc->receive_message();
+}
+
+static void conn_cb_wrapper(uv_poll_t* handle, int status, int events) {
+ static_cast(handle->data)->conn_cb(status, events);
+}
+
+static void ipc_cb_wrapper(uv_poll_t* handle, int status, int events) {
+ static_cast(handle->data)->ipc_cb(status, events);
+}
+
+static void signal_cb_wrapper(uv_signal_t* handle, int signum) {
+ g_terminate = 1;
+ g_reload = (signum == SIGUSR1);
+ uv_stop(handle->loop);
+}
+
+static void confwatch_cb_wrapper(uv_fs_event_t* handle, const char* fname, int events, int status) {
+ // TODO handle error
+ std::cout << fname << std::endl;
+ g_terminate = 1;
+ g_reload = 1;
+ uv_stop(handle->loop);
+}
+
/**
* Read events from configured file descriptors
*/
void controller::read_events() {
m_log.info("Entering event loop (thread-id=%lu)", this_thread::get_id());
- int fd_connection{-1};
- int fd_confwatch{-1};
- int fd_ipc{-1};
+ auto loop = uv_default_loop();
- vector fds;
- fds.emplace_back(*m_queuefd[PIPE_READ]);
- fds.emplace_back((fd_connection = m_connection.get_file_descriptor()));
+ auto conn_handle = std::make_unique();
+ uv_poll_init(loop, conn_handle.get(), m_connection.get_file_descriptor());
+ conn_handle->data = this;
+
+ uv_poll_start(conn_handle.get(), UV_READABLE, conn_cb_wrapper);
+
+ std::vector> handles;
+
+ for (auto s : {SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGALRM}) {
+ auto signal_handle = std::make_unique();
+ uv_signal_init(loop, signal_handle.get());
+ signal_handle->data = this;
+
+ uv_signal_start(signal_handle.get(), signal_cb_wrapper, s);
+ handles.push_back(std::move(signal_handle));
+ }
+
+ auto conf_handle = std::unique_ptr(nullptr);
if (m_confwatch) {
- m_log.trace("controller: Attach config watch");
- m_confwatch->attach(IN_MODIFY | IN_IGNORED);
- fds.emplace_back((fd_confwatch = m_confwatch->get_file_descriptor()));
+ conf_handle = std::make_unique();
+ uv_fs_event_init(loop, conf_handle.get());
+ conf_handle->data = this;
+
+ uv_fs_event_start(conf_handle.get(), confwatch_cb_wrapper, m_confwatch->path().c_str(), 0);
}
+ auto ipc_handle = std::unique_ptr(nullptr);
+
if (m_ipc) {
- fds.emplace_back((fd_ipc = m_ipc->get_file_descriptor()));
+ ipc_handle = std::make_unique();
+ uv_poll_init(loop, ipc_handle.get(), m_ipc->get_file_descriptor());
+ ipc_handle->data = this;
+ uv_poll_start(ipc_handle.get(), UV_READABLE, ipc_cb_wrapper);
}
- while (!g_terminate) {
- fd_set readfds{};
- FD_ZERO(&readfds);
-
- int maxfd{0};
- for (auto&& fd : fds) {
- FD_SET(fd, &readfds);
- maxfd = std::max(maxfd, fd);
- }
-
- // Wait until event is ready on one of the configured streams
- int events = select(maxfd + 1, &readfds, nullptr, nullptr, nullptr);
-
- // Check for errors
- if (events == -1) {
- /*
- * The Interrupt errno is generated when polybar is stopped, so it
- * shouldn't generate an error message
- */
- if (errno != EINTR) {
- m_log.err("select failed in event loop: %s", strerror(errno));
- }
-
- break;
- }
-
- if (g_terminate || m_connection.connection_has_error()) {
- break;
- }
-
- // Process event on the internal fd
- if (m_queuefd[PIPE_READ] && FD_ISSET(static_cast(*m_queuefd[PIPE_READ]), &readfds)) {
- char buffer[BUFSIZ];
- if (read(static_cast(*m_queuefd[PIPE_READ]), &buffer, BUFSIZ) == -1) {
- m_log.err("Failed to read from eventpipe (err: %s)", strerror(errno));
- }
- }
-
- // Process event on the config inotify watch fd
- unique_ptr confevent;
- if (fd_confwatch > -1 && FD_ISSET(fd_confwatch, &readfds) && (confevent = m_confwatch->await_match())) {
- if (confevent->mask & IN_IGNORED) {
- // IN_IGNORED: file was deleted or filesystem was unmounted
- //
- // This happens in some configurations of vim when a file is saved,
- // since it is not actually issuing calls to write() but rather
- // moves a file into the original's place after moving the original
- // file to a different location (and subsequently deleting it).
- //
- // We need to re-attach the watch to the new file in this case.
- fds.erase(
- std::remove_if(fds.begin(), fds.end(), [fd_confwatch](int fd) { return fd == fd_confwatch; }), fds.end());
- m_confwatch = inotify_util::make_watch(m_confwatch->path());
- m_confwatch->attach(IN_MODIFY | IN_IGNORED);
- fds.emplace_back((fd_confwatch = m_confwatch->get_file_descriptor()));
- }
- m_log.info("Configuration file changed");
- g_terminate = 1;
- g_reload = 1;
- }
-
- // Process event on the xcb connection fd
- if (fd_connection > -1 && FD_ISSET(fd_connection, &readfds)) {
- shared_ptr evt{};
- while ((evt = shared_ptr(xcb_poll_for_event(m_connection), free)) != nullptr) {
- try {
- m_connection.dispatch_event(evt);
- } catch (xpp::connection_error& err) {
- m_log.err("X connection error, terminating... (what: %s)", m_connection.error_str(err.code()));
- } catch (const exception& err) {
- m_log.err("Error in X event loop: %s", err.what());
- }
- }
- }
-
- // Process event on the ipc fd
- if (fd_ipc > -1 && FD_ISSET(fd_ipc, &readfds)) {
- m_ipc->receive_message();
- fds.erase(std::remove_if(fds.begin(), fds.end(), [fd_ipc](int fd) { return fd == fd_ipc; }), fds.end());
- fds.emplace_back((fd_ipc = m_ipc->get_file_descriptor()));
- }
- }
+ uv_run(loop, UV_RUN_DEFAULT);
+ uv_loop_close(loop);
}
/**
diff --git a/src/modules/xwindow.cpp b/src/modules/xwindow.cpp
index 4afde7d8..010750c1 100644
--- a/src/modules/xwindow.cpp
+++ b/src/modules/xwindow.cpp
@@ -1,11 +1,11 @@
#include "modules/xwindow.hpp"
+
#include "drawtypes/label.hpp"
+#include "modules/meta/base.inl"
#include "utils/factory.hpp"
#include "x11/atoms.hpp"
#include "x11/connection.hpp"
-#include "modules/meta/base.inl"
-
POLYBAR_NS
namespace modules {
@@ -138,6 +138,6 @@ namespace modules {
}
return false;
}
-}
+} // namespace modules
POLYBAR_NS_END