aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-11-03 15:51:46 +0800
committerYuqian Yang <crupest@crupest.life>2025-11-03 15:51:46 +0800
commit1e1ccbff5cc4f4ec7d870ea1db985b08e37ed3f8 (patch)
tree98962fdd8ca9a08098706b05acb4ed400b0a653a
parent9c897e8727d90345c2db7f36f52ab678778db936 (diff)
downloadcru-1e1ccbff5cc4f4ec7d870ea1db985b08e37ed3f8.tar.gz
cru-1e1ccbff5cc4f4ec7d870ea1db985b08e37ed3f8.tar.bz2
cru-1e1ccbff5cc4f4ec7d870ea1db985b08e37ed3f8.zip
Use TimerRegistry in UnixEventLoop.
-rw-r--r--include/cru/base/Timer.h6
-rw-r--r--include/cru/base/platform/unix/EventLoop.h40
-rw-r--r--src/base/platform/unix/EventLoop.cpp87
3 files changed, 38 insertions, 95 deletions
diff --git a/include/cru/base/Timer.h b/include/cru/base/Timer.h
index 80929f17..dac66c6a 100644
--- a/include/cru/base/Timer.h
+++ b/include/cru/base/Timer.h
@@ -83,13 +83,13 @@ class TimerRegistry : public Object2 {
}
/**
- * Returns 0 if there is no timer.
+ * Returns nullopt if there is no timer.
*/
- std::chrono::milliseconds NextTimeout(
+ std::optional<std::chrono::milliseconds> NextTimeout(
std::chrono::steady_clock::time_point now) {
std::unique_lock lock(mutex_);
- if (timers_.empty()) return std::chrono::milliseconds::zero();
+ if (timers_.empty()) return std::nullopt;
return std::ranges::min(
timers_ | std::views::transform([now](const TimerData& timer) {
diff --git a/include/cru/base/platform/unix/EventLoop.h b/include/cru/base/platform/unix/EventLoop.h
index 4f445fa3..697c6f37 100644
--- a/include/cru/base/platform/unix/EventLoop.h
+++ b/include/cru/base/platform/unix/EventLoop.h
@@ -6,6 +6,7 @@
#include "../../Base.h"
#include "../../Exception.h"
+#include "../../Timer.h"
#include "UnixFile.h"
#include <poll.h>
@@ -54,27 +55,43 @@ class UnixEventLoop : public Object2 {
UnixEventLoop();
/**
- * The only thread-safe function.
+ * Thread-safe.
*/
void QueueAction(std::function<void()> action);
int Run();
void RequestQuit(int exit_code = 0);
+ /**
+ * Thread-safe.
+ */
int SetTimer(std::function<void()> action, std::chrono::milliseconds timeout,
bool repeat);
+
+ /**
+ * Thread-safe.
+ */
void CancelTimer(int id);
+ /**
+ * Thread-safe.
+ */
int SetImmediate(std::function<void()> action) {
return this->SetTimer(std::move(action), std::chrono::milliseconds::zero(),
false);
}
+ /**
+ * Thread-safe.
+ */
int SetTimeout(std::function<void()> action,
std::chrono::milliseconds timeout) {
return this->SetTimer(std::move(action), std::move(timeout), false);
}
+ /**
+ * Thread-safe.
+ */
int SetInterval(std::function<void()> action,
std::chrono::milliseconds interval) {
return this->SetTimer(std::move(action), std::move(interval), true);
@@ -84,25 +101,7 @@ class UnixEventLoop : public Object2 {
void RemovePoll(int fd);
private:
- struct TimerData {
- int id;
- std::chrono::milliseconds original_timeout;
- std::chrono::milliseconds timeout;
- bool repeat;
- std::function<void()> action;
-
- TimerData(int id, std::chrono::milliseconds timeout, bool repeat,
- std::function<void()> action)
- : id(id),
- original_timeout(timeout),
- timeout(timeout),
- repeat(repeat),
- action(std::move(action)) {}
- };
-
- private:
bool CheckPoll();
- bool CheckTimer();
bool CheckActionPipe();
private:
@@ -111,8 +110,7 @@ class UnixEventLoop : public Object2 {
std::vector<pollfd> polls_;
std::vector<PollHandler> poll_actions_;
- int timer_tag_;
- std::vector<TimerData> timers_;
+ TimerRegistry<std::function<void()>> timer_registry_;
UnixFileDescriptor action_pipe_read_end_;
UnixFileDescriptor action_pipe_write_end_;
diff --git a/src/base/platform/unix/EventLoop.cpp b/src/base/platform/unix/EventLoop.cpp
index 6e8dc16e..86c0deb9 100644
--- a/src/base/platform/unix/EventLoop.cpp
+++ b/src/base/platform/unix/EventLoop.cpp
@@ -10,7 +10,7 @@
namespace cru::platform::unix {
int UnixTimerFile::GetReadFd() const { return this->read_fd_; }
-UnixEventLoop::UnixEventLoop() : polls_(1), poll_actions_(1), timer_tag_(1) {
+UnixEventLoop::UnixEventLoop() : polls_(1), poll_actions_(1) {
auto action_pipe = OpenUniDirectionalPipe(UnixPipeFlags::NonBlock);
action_pipe_read_end_ = std::move(action_pipe.read);
action_pipe_write_end_ = std::move(action_pipe.write);
@@ -38,7 +38,9 @@ int UnixEventLoop::Run() {
continue;
}
- if (CheckTimer()) {
+ auto now = std::chrono::steady_clock::now();
+ if (auto result = timer_registry_.Update(now)) {
+ result->data();
continue;
}
@@ -46,72 +48,39 @@ int UnixEventLoop::Run() {
continue;
}
- if (!timers_.empty()) {
- poll_timeout =
- std::ranges::min_element(timers_, [](const TimerData &left,
- const TimerData &right) {
- return left.timeout < right.timeout;
- })->timeout.count();
+ if (auto next_timeout = timer_registry_.NextTimeout(now)) {
+ poll_timeout = next_timeout->count();
}
- auto start = std::chrono::steady_clock::now();
-
auto result = ::poll(polls_.data(), polls_.size(), poll_timeout);
if (result < 0) {
throw Exception("Failed to poll in event loop.");
}
-
- auto end = std::chrono::steady_clock::now();
- auto time =
- std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
-
- for (auto &timer : timers_) {
- timer.timeout -= time;
- }
}
return exit_code_.value();
}
-void UnixEventLoop::RequestQuit(int exit_code) { exit_code_ = exit_code; }
+void UnixEventLoop::RequestQuit(int exit_code) {
+ QueueAction([this, exit_code] { exit_code_ = exit_code; });
+}
int UnixEventLoop::SetTimer(std::function<void()> action,
std::chrono::milliseconds timeout, bool repeat) {
- if (repeat) {
- if (timeout <= std::chrono::milliseconds::zero()) {
- throw Exception("Interval must be bigger than 0.");
- }
- } else {
- if (timeout < std::chrono::milliseconds::zero()) {
- throw Exception("Timeout must be at least 0.");
- }
- }
-
- auto tag = timer_tag_++;
-
- timers_.push_back(
- TimerData(tag, std::move(timeout), repeat, std::move(action)));
-
- return tag;
+ return timer_registry_.Add(std::move(action), timeout, repeat);
}
-void UnixEventLoop::CancelTimer(int id) {
- auto iter = std::ranges::find_if(
- timers_, [id](const TimerData &timer) { return timer.id == id; });
- if (iter != timers_.cend()) {
- timers_.erase(iter);
- }
-}
+void UnixEventLoop::CancelTimer(int id) { timer_registry_.Remove(id); }
bool UnixEventLoop::CheckPoll() {
auto iter = std::ranges::find_if(
- polls_, [](const pollfd &poll_fd) { return poll_fd.revents != 0; });
+ polls_, [](const pollfd& poll_fd) { return poll_fd.revents != 0; });
if (iter == polls_.cend()) {
return false;
}
- auto &revents = iter->revents;
+ auto& revents = iter->revents;
if (revents != 0) {
poll_actions_[iter - polls_.cbegin()](revents);
}
@@ -120,32 +89,8 @@ bool UnixEventLoop::CheckPoll() {
return true;
}
-bool UnixEventLoop::CheckTimer() {
- auto iter = std::ranges::find_if(timers_, [](const TimerData &timer) {
- return timer.timeout <= std::chrono::milliseconds::zero();
- });
-
- if (iter == timers_.end()) {
- return false;
- }
-
- auto &timer = *iter;
- if (timer.repeat) {
- while (timer.timeout <= std::chrono::milliseconds::zero()) {
- timer.timeout += timer.original_timeout;
- timer.action();
- }
- } else {
- auto action = timer.action;
- timers_.erase(iter);
- action();
- }
-
- return true;
-}
-
bool UnixEventLoop::CheckActionPipe() {
- std::function<void()> *pointer;
+ std::function<void()>* pointer;
constexpr size_t pointer_size = sizeof(decltype(pointer));
auto rest = pointer_size;
while (rest > 0) {
@@ -174,7 +119,7 @@ bool UnixEventLoop::CheckActionPipe() {
}
void UnixEventLoop::SetPoll(int fd, PollEvents events, PollHandler action) {
- for (auto &poll_fd : polls_) {
+ for (auto& poll_fd : polls_) {
if (poll_fd.fd == fd) {
poll_fd.events = events;
return;
@@ -191,7 +136,7 @@ void UnixEventLoop::SetPoll(int fd, PollEvents events, PollHandler action) {
void UnixEventLoop::RemovePoll(int fd) {
auto iter = std::ranges::find_if(
- polls_, [fd](const pollfd &poll_fd) { return poll_fd.fd == fd; });
+ polls_, [fd](const pollfd& poll_fd) { return poll_fd.fd == fd; });
if (iter != polls_.cend()) {
auto index = iter - polls_.cbegin();
polls_.erase(iter);