refactor(mainloop): cleaner approach via pollSource to absract as much service code as possible

This commit is contained in:
Lemmy
2026-04-03 20:56:36 -04:00
parent 5ed0ee9028
commit 7409885a16
10 changed files with 203 additions and 85 deletions
+16 -2
View File
@@ -1,5 +1,6 @@
#include "Application.hpp"
#include "app/PollSource.hpp"
#include "core/Log.hpp"
#include <csignal>
@@ -119,8 +120,21 @@ void Application::run() {
}
}
m_mainLoop = std::make_unique<MainLoop>(m_wayland, m_bar, m_bus.get(), m_notificationService.get(),
&m_timeService, &m_configService, &m_stateService);
// Build poll sources
std::vector<PollSource*> sources;
if (m_bus != nullptr) {
m_busPollSource = std::make_unique<SessionBusPollSource>(*m_bus);
sources.push_back(m_busPollSource.get());
}
if (m_notificationService != nullptr) {
m_notificationPollSource = std::make_unique<NotificationPollSource>(*m_notificationService);
sources.push_back(m_notificationPollSource.get());
}
sources.push_back(&m_timePollSource);
sources.push_back(&m_configPollSource);
sources.push_back(&m_statePollSource);
m_mainLoop = std::make_unique<MainLoop>(m_wayland, m_bar, std::move(sources));
m_mainLoop->run();
logInfo("shutdown");
+14 -1
View File
@@ -1,17 +1,22 @@
#pragma once
#include "app/MainLoop.hpp"
#include "config/ConfigPollSource.hpp"
#include "config/ConfigService.hpp"
#include "config/StatePollSource.hpp"
#include "config/StateService.hpp"
#include "debug/DebugService.hpp"
#include "dbus/SessionBus.hpp"
#include "dbus/SessionBusPollSource.hpp"
#include "dbus/mpris/MprisService.hpp"
#include "dbus/notification/NotificationPollSource.hpp"
#include "dbus/notification/NotificationService.hpp"
#include "debug/DebugService.hpp"
#include "notification/InternalNotificationService.hpp"
#include "notification/NotificationManager.hpp"
#include "shell/Bar.hpp"
#include "shell/Wallpaper.hpp"
#include "system/SystemMonitorService.hpp"
#include "time/TimePollSource.hpp"
#include "time/TimeService.hpp"
#include "wayland/WaylandConnection.hpp"
@@ -42,5 +47,13 @@ private:
NotificationManager m_manager;
InternalNotificationService m_internalNotifications;
std::unique_ptr<NotificationService> m_notificationService;
// Poll sources (must outlive MainLoop)
std::unique_ptr<SessionBusPollSource> m_busPollSource;
std::unique_ptr<NotificationPollSource> m_notificationPollSource;
TimePollSource m_timePollSource{m_timeService};
ConfigPollSource m_configPollSource{m_configService};
StatePollSource m_statePollSource{m_stateService};
std::unique_ptr<MainLoop> m_mainLoop;
};
+18 -69
View File
@@ -1,13 +1,9 @@
#include "app/MainLoop.hpp"
#include "app/Application.hpp"
#include "config/ConfigService.hpp"
#include "config/StateService.hpp"
#include "app/PollSource.hpp"
#include "core/Log.hpp"
#include "dbus/SessionBus.hpp"
#include "dbus/notification/NotificationService.hpp"
#include "shell/Bar.hpp"
#include "time/TimeService.hpp"
#include "wayland/WaylandConnection.hpp"
#include <cerrno>
@@ -16,16 +12,10 @@
#include <wayland-client-core.h>
MainLoop::MainLoop(WaylandConnection& wayland, Bar& bar, SessionBus* bus,
NotificationService* notifications, TimeService* time,
ConfigService* config, StateService* state)
MainLoop::MainLoop(WaylandConnection& wayland, Bar& bar, std::vector<PollSource*> sources)
: m_wayland(wayland)
, m_bar(bar)
, m_bus(bus)
, m_notifications(notifications)
, m_time(time)
, m_config(config)
, m_state(state) {}
, m_sources(std::move(sources)) {}
void MainLoop::run() {
while (m_bar.isRunning() && !Application::s_shutdown_requested) {
@@ -36,46 +26,25 @@ void MainLoop::run() {
throw std::runtime_error("failed to flush Wayland display");
}
// Check if shutdown was requested since the last iteration
if (Application::s_shutdown_requested) {
break;
}
int pollTimeout = -1;
if (m_time != nullptr) {
pollTimeout = m_time->pollTimeoutMs();
}
if (m_notifications != nullptr) {
const int expiryTimeout = m_notifications->nextExpiryTimeoutMs();
if (expiryTimeout >= 0 && (pollTimeout < 0 || expiryTimeout < pollTimeout)) {
pollTimeout = expiryTimeout;
}
}
// Build poll fd list
// Collect poll fds and compute timeout from all sources
std::vector<pollfd> pollFds;
pollFds.push_back({.fd = wl_display_get_fd(m_wayland.display()), .events = POLLIN, .revents = 0});
if (m_bus != nullptr) {
auto dbusPollData = m_bus->getPollData();
const int dbusTimeout = dbusPollData.getPollTimeout();
if (dbusTimeout >= 0 && (pollTimeout < 0 || dbusTimeout < pollTimeout)) {
pollTimeout = dbusTimeout;
int pollTimeout = -1;
std::vector<std::size_t> sourceStartIndices;
sourceStartIndices.reserve(m_sources.size());
for (auto* source : m_sources) {
sourceStartIndices.push_back(source->addPollFds(pollFds));
const int t = source->pollTimeoutMs();
if (t >= 0 && (pollTimeout < 0 || t < pollTimeout)) {
pollTimeout = t;
}
pollFds.push_back({.fd = dbusPollData.fd, .events = dbusPollData.events, .revents = 0});
pollFds.push_back({.fd = dbusPollData.eventFd, .events = POLLIN, .revents = 0});
}
int configFdIdx = -1;
if (m_config != nullptr && m_config->watchFd() >= 0) {
configFdIdx = static_cast<int>(pollFds.size());
pollFds.push_back({.fd = m_config->watchFd(), .events = POLLIN, .revents = 0});
}
int stateFdIdx = -1;
if (m_state != nullptr && m_state->watchFd() >= 0) {
stateFdIdx = static_cast<int>(pollFds.size());
pollFds.push_back({.fd = m_state->watchFd(), .events = POLLIN, .revents = 0});
}
const int pollResult = ::poll(pollFds.data(), pollFds.size(), pollTimeout);
@@ -97,36 +66,16 @@ void MainLoop::run() {
}
}
// Dispatch D-Bus events
if (m_bus != nullptr) {
m_bus->processPendingEvents();
}
// Check config file changes
if (configFdIdx >= 0 && (pollFds[static_cast<std::size_t>(configFdIdx)].revents & POLLIN) != 0) {
m_config->checkReload();
}
// Check state file changes
if (stateFdIdx >= 0 && (pollFds[static_cast<std::size_t>(stateFdIdx)].revents & POLLIN) != 0) {
m_state->checkReload();
}
if (m_time != nullptr) {
m_time->tick();
}
if (m_notifications != nullptr) {
m_notifications->processExpiredNotifications();
// Dispatch all sources
for (std::size_t i = 0; i < m_sources.size(); ++i) {
m_sources[i]->dispatch(pollFds, sourceStartIndices[i]);
}
}
// Close all UI surfaces immediately and flush Wayland to make them disappear
// This happens while we're still in a valid Wayland/display context
logDebug("closing bar surfaces for clean shutdown");
m_bar.closeAllInstances();
// Dispatch any pending surface destroy messages and flush to compositor
if (wl_display_dispatch_pending(m_wayland.display()) < 0) {
logWarn("failed to dispatch pending Wayland events during shutdown");
}
+5 -13
View File
@@ -1,27 +1,19 @@
#pragma once
#include <vector>
class Bar;
class ConfigService;
class NotificationService;
class SessionBus;
class StateService;
class TimeService;
class PollSource;
class WaylandConnection;
class MainLoop {
public:
MainLoop(WaylandConnection& wayland, Bar& bar, SessionBus* bus,
NotificationService* notifications, TimeService* time,
ConfigService* config, StateService* state);
MainLoop(WaylandConnection& wayland, Bar& bar, std::vector<PollSource*> sources);
void run();
private:
WaylandConnection& m_wayland;
Bar& m_bar;
SessionBus* m_bus = nullptr;
NotificationService* m_notifications = nullptr;
TimeService* m_time = nullptr;
ConfigService* m_config = nullptr;
StateService* m_state = nullptr;
std::vector<PollSource*> m_sources;
};
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <poll.h>
#include <vector>
class PollSource {
public:
virtual ~PollSource() = default;
// Append any fds this source wants polled. Returns the starting index.
std::size_t addPollFds(std::vector<pollfd>& fds) {
auto start = fds.size();
doAddPollFds(fds);
return start;
}
// Minimum timeout this source needs, or -1 for no timeout.
[[nodiscard]] virtual int pollTimeoutMs() const { return -1; }
// Called after poll() returns. startIdx is what addPollFds returned.
virtual void dispatch(const std::vector<pollfd>& fds, std::size_t startIdx) = 0;
protected:
virtual void doAddPollFds(std::vector<pollfd>& fds) = 0;
};
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include "app/PollSource.hpp"
#include "config/ConfigService.hpp"
class ConfigPollSource final : public PollSource {
public:
explicit ConfigPollSource(ConfigService& config) : m_config(config) {}
void dispatch(const std::vector<pollfd>& fds, std::size_t startIdx) override {
if (m_config.watchFd() >= 0 && (fds[startIdx].revents & POLLIN) != 0) {
m_config.checkReload();
}
}
protected:
void doAddPollFds(std::vector<pollfd>& fds) override {
if (m_config.watchFd() >= 0) {
fds.push_back({.fd = m_config.watchFd(), .events = POLLIN, .revents = 0});
}
}
private:
ConfigService& m_config;
};
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include "app/PollSource.hpp"
#include "config/StateService.hpp"
class StatePollSource final : public PollSource {
public:
explicit StatePollSource(StateService& state) : m_state(state) {}
void dispatch(const std::vector<pollfd>& fds, std::size_t startIdx) override {
if (m_state.watchFd() >= 0 && (fds[startIdx].revents & POLLIN) != 0) {
m_state.checkReload();
}
}
protected:
void doAddPollFds(std::vector<pollfd>& fds) override {
if (m_state.watchFd() >= 0) {
fds.push_back({.fd = m_state.watchFd(), .events = POLLIN, .revents = 0});
}
}
private:
StateService& m_state;
};
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "app/PollSource.hpp"
#include "dbus/SessionBus.hpp"
class SessionBusPollSource final : public PollSource {
public:
explicit SessionBusPollSource(SessionBus& bus) : m_bus(bus) {}
[[nodiscard]] int pollTimeoutMs() const override {
const int t = m_bus.getPollData().getPollTimeout();
return t;
}
void dispatch(const std::vector<pollfd>& /*fds*/, std::size_t /*startIdx*/) override {
m_bus.processPendingEvents();
}
protected:
void doAddPollFds(std::vector<pollfd>& fds) override {
auto pd = m_bus.getPollData();
fds.push_back({.fd = pd.fd, .events = pd.events, .revents = 0});
fds.push_back({.fd = pd.eventFd, .events = POLLIN, .revents = 0});
}
private:
SessionBus& m_bus;
};
@@ -0,0 +1,24 @@
#pragma once
#include "app/PollSource.hpp"
#include "dbus/notification/NotificationService.hpp"
class NotificationPollSource final : public PollSource {
public:
explicit NotificationPollSource(NotificationService& notifications)
: m_notifications(notifications) {}
[[nodiscard]] int pollTimeoutMs() const override {
return m_notifications.nextExpiryTimeoutMs();
}
void dispatch(const std::vector<pollfd>& /*fds*/, std::size_t /*startIdx*/) override {
m_notifications.processExpiredNotifications();
}
protected:
void doAddPollFds(std::vector<pollfd>& /*fds*/) override {}
private:
NotificationService& m_notifications;
};
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "app/PollSource.hpp"
#include "time/TimeService.hpp"
class TimePollSource final : public PollSource {
public:
explicit TimePollSource(TimeService& time) : m_time(time) {}
[[nodiscard]] int pollTimeoutMs() const override {
return m_time.pollTimeoutMs();
}
void dispatch(const std::vector<pollfd>& /*fds*/, std::size_t /*startIdx*/) override {
m_time.tick();
}
protected:
void doAddPollFds(std::vector<pollfd>& /*fds*/) override {}
private:
TimeService& m_time;
};