From 227118866190a7fe17b42e8c589c475781c69f33 Mon Sep 17 00:00:00 2001 From: Yuqian Yang Date: Fri, 5 Sep 2025 23:52:33 +0800 Subject: Event loop poll. --- include/cru/base/platform/unix/EventLoop.h | 16 ++++-- include/cru/base/platform/unix/UnixFile.h | 4 ++ src/base/platform/unix/EventLoop.cpp | 83 ++++++++++++++++++++++-------- src/base/platform/unix/UnixFile.cpp | 13 +++++ 4 files changed, 90 insertions(+), 26 deletions(-) diff --git a/include/cru/base/platform/unix/EventLoop.h b/include/cru/base/platform/unix/EventLoop.h index b735ef26..784cb780 100644 --- a/include/cru/base/platform/unix/EventLoop.h +++ b/include/cru/base/platform/unix/EventLoop.h @@ -1,6 +1,5 @@ #pragma once -#include #if !defined(__unix) && !defined(__APPLE__) #error "This file can only be included on unix." #endif @@ -9,9 +8,12 @@ #include "../../Exception.h" #include "UnixFile.h" +#include #include #include +#include #include +#include namespace cru::platform::unix { class UnixTimerFile : public Object2 { @@ -84,13 +86,19 @@ class UnixEventLoop : public Object2 { action(std::move(action)) {} }; -private: + private: + bool CheckPoll(); + bool CheckTimer(); bool ReadTimerPipe(); -private: - + private: std::thread::id running_thread_; + std::vector polls_; + std::vector< + std::function().revents) revent)>> + poll_actions_; + std::atomic_int timer_tag_; std::vector timers_; diff --git a/include/cru/base/platform/unix/UnixFile.h b/include/cru/base/platform/unix/UnixFile.h index a5f1eb81..5754b07b 100644 --- a/include/cru/base/platform/unix/UnixFile.h +++ b/include/cru/base/platform/unix/UnixFile.h @@ -41,6 +41,10 @@ class UnixFileDescriptor { * If O_NONBLOCK is set and EAGAIN or EWOULDBLOCK is returned, -1 is returned. */ ssize_t Read(void* buffer, size_t size); + /** + * If O_NONBLOCK is set and EAGAIN or EWOULDBLOCK is returned, -1 is returned. + */ + ssize_t Write(const void* buffer, size_t size); void SetFileDescriptorFlags(int flags); private: diff --git a/src/base/platform/unix/EventLoop.cpp b/src/base/platform/unix/EventLoop.cpp index 0aac4134..3c645a85 100644 --- a/src/base/platform/unix/EventLoop.cpp +++ b/src/base/platform/unix/EventLoop.cpp @@ -9,31 +9,26 @@ namespace cru::platform::unix { int UnixTimerFile::GetReadFd() const { return this->read_fd_; } -UnixEventLoop::UnixEventLoop() : timer_tag_(1) {} +UnixEventLoop::UnixEventLoop() : timer_tag_(1), polls_(1), poll_actions_(1) { + auto timer_pipe = OpenUniDirectionalPipe(UnixPipeFlags::NonBlock); + timer_pipe_read_end_ = std::move(timer_pipe.read); + timer_pipe_write_end_ = std::move(timer_pipe.write); + polls_[0].fd = timer_pipe_read_end_; + polls_[0].events = POLLIN; + poll_actions_[0] = [](auto _) {}; +} int UnixEventLoop::Run() { running_thread_ = std::this_thread::get_id(); - pollfd poll_fds[1]; - while (!exit_code_) { int poll_timeout = -1; - auto iter = std::ranges::find_if(timers_, [](const TimerData &timer) { - return timer.timeout <= std::chrono::milliseconds::zero(); - }); - if (iter != timers_.end()) { - 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(); - } + while (CheckPoll()) { + continue; + } + + while (CheckTimer()) { continue; } @@ -51,9 +46,10 @@ int UnixEventLoop::Run() { auto start = std::chrono::steady_clock::now(); - ::poll(poll_fds, sizeof poll_fds / sizeof *poll_fds, poll_timeout); - - // TODO: A Big Implement to handle X events. + 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 = @@ -87,12 +83,55 @@ int UnixEventLoop::SetTimer(std::function action, timers_.push_back( TimerData(tag, std::move(timeout), repeat, std::move(action))); } else { - // TODO: Implement + auto timer = + new TimerData(tag, std::move(timeout), repeat, std::move(action)); + timer_pipe_write_end_.Write(&timer, sizeof(decltype(timer))); } return tag; } +bool UnixEventLoop::CheckPoll() { + auto iter = std::ranges::find_if( + polls_, [](const pollfd &poll_fd) { return poll_fd.revents != 0; }); + + if (iter == polls_.cend()) { + return false; + } + + auto &revents = iter->revents; + if (revents != 0) { + poll_actions_[iter - polls_.cbegin()](revents); + } + revents = 0; + + 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::ReadTimerPipe() { TimerData *pointer; constexpr size_t pointer_size = sizeof(decltype(pointer)); diff --git a/src/base/platform/unix/UnixFile.cpp b/src/base/platform/unix/UnixFile.cpp index 6d2dea89..9d4db949 100644 --- a/src/base/platform/unix/UnixFile.cpp +++ b/src/base/platform/unix/UnixFile.cpp @@ -96,6 +96,19 @@ ssize_t UnixFileDescriptor::Read(void* buffer, size_t size) { return result; } +ssize_t UnixFileDescriptor::Write(const void* buffer, size_t size) { + EnsureValid(); + auto result = ::write(GetValue(), buffer, size); + if (result == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return -1; + } else { + throw ErrnoException("Failed to write on file descriptor."); + } + } + return result; +} + void UnixFileDescriptor::SetFileDescriptorFlags(int flags) { EnsureValid(); if (::fcntl(GetValue(), F_SETFL, flags) == -1) { -- cgit v1.2.3