Use libuv for the controller's event loop
This commit is contained in:
parent
0bf45f3bd6
commit
249c3ec141
@ -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)
|
||||
|
13
cmake/modules/FindLibUV.cmake
Normal file
13
cmake/modules/FindLibUV.cmake
Normal file
@ -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()
|
@ -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<ipc> m_ipc;
|
||||
unique_ptr<inotify_watch> m_confwatch;
|
||||
|
||||
array<unique_ptr<file_descriptor>, 2> m_queuefd{};
|
||||
|
||||
/**
|
||||
* \brief State flag
|
||||
*/
|
||||
|
@ -169,6 +169,19 @@ namespace modules {
|
||||
void sleep(chrono::duration<double> duration);
|
||||
template <class Clock, class Duration>
|
||||
void sleep_until(chrono::time_point<Clock, Duration> 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();
|
||||
|
@ -151,6 +151,7 @@ if(BUILD_LIBPOLY)
|
||||
Cairo::CairoFC
|
||||
moodycamel
|
||||
xpp
|
||||
LibUV::LibUV
|
||||
)
|
||||
|
||||
if (TARGET i3ipc++)
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "components/controller.hpp"
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <utility>
|
||||
|
||||
@ -25,22 +27,9 @@
|
||||
|
||||
POLYBAR_NS
|
||||
|
||||
array<int, 2> 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<file_descriptor>(g_eventpipe[PIPE_READ]);
|
||||
m_queuefd[PIPE_WRITE] = make_unique<file_descriptor>(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,93 +172,16 @@ bool controller::enqueue(string&& input_data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read events from configured file descriptors
|
||||
*/
|
||||
void controller::read_events() {
|
||||
m_log.info("Entering event loop (thread-id=%lu)", this_thread::get_id());
|
||||
void controller::conn_cb(int status, int events) {
|
||||
// TODO handle negative status
|
||||
|
||||
int fd_connection{-1};
|
||||
int fd_confwatch{-1};
|
||||
int fd_ipc{-1};
|
||||
|
||||
vector<int> fds;
|
||||
fds.emplace_back(*m_queuefd[PIPE_READ]);
|
||||
fds.emplace_back((fd_connection = m_connection.get_file_descriptor()));
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
if (m_ipc) {
|
||||
fds.emplace_back((fd_ipc = m_ipc->get_file_descriptor()));
|
||||
}
|
||||
|
||||
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<int>(*m_queuefd[PIPE_READ]), &readfds)) {
|
||||
char buffer[BUFSIZ];
|
||||
if (read(static_cast<int>(*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<inotify_event> 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");
|
||||
if (m_connection.connection_has_error()) {
|
||||
g_terminate = 1;
|
||||
g_reload = 1;
|
||||
g_reload = 0;
|
||||
uv_stop(uv_default_loop());
|
||||
return;
|
||||
}
|
||||
|
||||
// Process event on the xcb connection fd
|
||||
if (fd_connection > -1 && FD_ISSET(fd_connection, &readfds)) {
|
||||
shared_ptr<xcb_generic_event_t> evt{};
|
||||
while ((evt = shared_ptr<xcb_generic_event_t>(xcb_poll_for_event(m_connection), free)) != nullptr) {
|
||||
try {
|
||||
@ -310,13 +194,79 @@ void controller::read_events() {
|
||||
}
|
||||
}
|
||||
|
||||
// Process event on the ipc fd
|
||||
if (fd_ipc > -1 && FD_ISSET(fd_ipc, &readfds)) {
|
||||
void controller::ipc_cb(int status, int events) {
|
||||
// TODO handle negative status
|
||||
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()));
|
||||
}
|
||||
|
||||
static void conn_cb_wrapper(uv_poll_t* handle, int status, int events) {
|
||||
static_cast<controller*>(handle->data)->conn_cb(status, events);
|
||||
}
|
||||
|
||||
static void ipc_cb_wrapper(uv_poll_t* handle, int status, int events) {
|
||||
static_cast<controller*>(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());
|
||||
|
||||
auto loop = uv_default_loop();
|
||||
|
||||
auto conn_handle = std::make_unique<uv_poll_t>();
|
||||
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<unique_ptr<uv_signal_t>> handles;
|
||||
|
||||
for (auto s : {SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGALRM}) {
|
||||
auto signal_handle = std::make_unique<uv_signal_t>();
|
||||
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<uv_fs_event_t>(nullptr);
|
||||
|
||||
if (m_confwatch) {
|
||||
conf_handle = std::make_unique<uv_fs_event_t>();
|
||||
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<uv_poll_t>(nullptr);
|
||||
|
||||
if (m_ipc) {
|
||||
ipc_handle = std::make_unique<uv_poll_t>();
|
||||
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);
|
||||
}
|
||||
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
uv_loop_close(loop);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user