From f81b0811a879d4a54ff697b31627406620d7dfc4 Mon Sep 17 00:00:00 2001 From: Theis Pieter Hollebeek Date: Wed, 25 Mar 2026 14:08:43 +0100 Subject: [PATCH] backend: poll/wake impl --- backend/src/errno_shim.hpp | 12 +++++ backend/src/event_loop.cpp | 53 ++++++++++++++++++++++ backend/src/event_loop.hpp | 33 ++++++++++++++ backend/src/main.cpp | 10 ++--- backend/src/result.hpp | 5 +++ backend/src/server.cpp | 61 +++++++++++++++++++++++++ backend/src/server.hpp | 35 +++++++++++++++ backend/src/tcp.cpp | 92 ++++---------------------------------- backend/src/tcp.hpp | 31 +++---------- 9 files changed, 218 insertions(+), 114 deletions(-) create mode 100644 backend/src/errno_shim.hpp create mode 100644 backend/src/event_loop.cpp create mode 100644 backend/src/event_loop.hpp create mode 100644 backend/src/result.hpp diff --git a/backend/src/errno_shim.hpp b/backend/src/errno_shim.hpp new file mode 100644 index 0000000..9646f47 --- /dev/null +++ b/backend/src/errno_shim.hpp @@ -0,0 +1,12 @@ +#pragma once +#include +#include +#include +#include + +namespace mst { +auto inline errno_shim(std::string_view message) -> std::string +{ + return std::format("{} ({})", message, strerror(errno)); +} +} diff --git a/backend/src/event_loop.cpp b/backend/src/event_loop.cpp new file mode 100644 index 0000000..ace6c90 --- /dev/null +++ b/backend/src/event_loop.cpp @@ -0,0 +1,53 @@ +#include "event_loop.hpp" +#include "errno_shim.hpp" +#include "server.hpp" +#include +#include + +namespace mst::event { + +auto Manager::start() -> Result +{ + epoll_event events[128] = { }; + while (true) { + auto events_len = ::epoll_wait(this->epoll_fd, events, 128, -1); + if (events_len < 0) { + return std::unexpected(errno_shim("could not poll")); + } + for (int i = 0; i < events_len; ++i) { + auto event = (event::Event*)events[i].data.ptr; + + switch (event->variant) { + case event::Server: { + auto ptr = (mst::Server*)event->data; + auto res = ptr->wake(*this); + if (!res) { + return std::unexpected(res.error()); + } + + break; + } + case event::Client: { + std::println("client call scheduled"); + auto ptr = (mst::Client*)event->data; + auto res = ptr->wake(); + if (!res) { + return std::unexpected(res.error()); + } + break; + } + } + } + } +} + +auto Manager::create() -> Result +{ + auto epoll_fd = ::epoll_create1(0); + if (epoll_fd < 0) { + std::unexpected(mst::errno_shim("could not create epoll")); + } + + return Manager(epoll_fd); +} +} diff --git a/backend/src/event_loop.hpp b/backend/src/event_loop.hpp new file mode 100644 index 0000000..aac3358 --- /dev/null +++ b/backend/src/event_loop.hpp @@ -0,0 +1,33 @@ +#pragma once +#include "result.hpp" +namespace mst::event { +enum Variant { Server, Client }; +struct Event { + Variant variant; + int fd; + void* data; +}; + +template auto make_event(Variant variant, Data data) -> Event* +{ + auto event = (Event*)malloc(sizeof(Event)); + auto ptr = (Data*)std::malloc(sizeof(Data)); + *ptr = data; + + event->variant = variant; + event->data = ptr; + + return event; +} + +class Manager { +public: + auto start() -> Result; + int epoll_fd; + static auto create() -> Result; + +private: + Manager(int epoll_fd) + : epoll_fd(epoll_fd) { }; +}; +} diff --git a/backend/src/main.cpp b/backend/src/main.cpp index ddc550c..fa7565f 100644 --- a/backend/src/main.cpp +++ b/backend/src/main.cpp @@ -1,6 +1,6 @@ -#include "tcp.hpp" +#include "event_loop.hpp" +#include "server.hpp" #include -#include #include #include #include @@ -9,15 +9,15 @@ int main(void) { - auto x = mst::TcpListener::bind("0.0.0.0", PORT); + auto mgr = mst::event::Manager::create().value(); + auto x = mst::Server::bind(mgr, "0.0.0.0", PORT); if (!x) { std::println("{}", x.error()); return 1; } std::println("starting"); - auto listener = x.value(); { - auto x = listener.loop(); + auto x = mgr.start(); } return 0; } diff --git a/backend/src/result.hpp b/backend/src/result.hpp new file mode 100644 index 0000000..ebd8a58 --- /dev/null +++ b/backend/src/result.hpp @@ -0,0 +1,5 @@ +#pragma once +#include +#include + +template using Result = std::expected; diff --git a/backend/src/server.cpp b/backend/src/server.cpp index e69de29..b1ea4cb 100644 --- a/backend/src/server.cpp +++ b/backend/src/server.cpp @@ -0,0 +1,61 @@ +#include "server.hpp" +#include "errno_shim.hpp" +#include "event_loop.hpp" +#include +#include + +namespace mst { + +auto Client::wake() -> Result +{ + uint8_t buffer[128] = { }; + auto x = this->connection.read(buffer, 128); + if (!x) { + return std::unexpected(x.error()); + } + auto bytes_read = x.value(); + for (size_t i = 0; i < bytes_read; ++i) { + std::println("{:c}", buffer[i]); + } + + return { }; +} + +auto Server::bind(mst::event::Manager& mgr, const std::string& host, + uint16_t port) -> Result +{ + auto x = TcpListener::bind(host, port); + if (!x) { + return std::unexpected(x.error()); + } + auto listener = x.value(); + auto context = event::make_event(event::Server, Server(listener)); + auto poll_event + = epoll_event { .events = EPOLLIN, .data = { .ptr = context } }; + if (::epoll_ctl(mgr.epoll_fd, EPOLL_CTL_ADD, listener.fd, &poll_event) + < 0) { + return std::unexpected(errno_shim("could not add listener to epoll")); + } + return { }; +} + +auto Server::wake(event::Manager& mgr) -> Result +{ + auto x = this->listener.accept(); + auto connection = x.value(); + + auto context + = event::make_event(event::Client, mst::Client(*this, connection)); + + auto poll_event + = epoll_event { .events = EPOLLIN, .data = { .ptr = context } }; + + if (::epoll_ctl(mgr.epoll_fd, EPOLL_CTL_ADD, connection.fd, &poll_event) + < 0) { + return std::unexpected( + mst::errno_shim("could not add connection to epoll")); + } + return { }; +} + +} diff --git a/backend/src/server.hpp b/backend/src/server.hpp index 6f70f09..351bdb6 100644 --- a/backend/src/server.hpp +++ b/backend/src/server.hpp @@ -1 +1,36 @@ #pragma once + +#include "event_loop.hpp" +#include "tcp.hpp" + +namespace mst { + +class Server { +public: + auto wake(mst::event::Manager& mgr) -> Result; + static auto bind(mst::event::Manager& mgr, const std::string& host, + uint16_t port) -> Result; + +private: + TcpListener listener; + + Server(TcpListener listener) + : listener(listener) + { + } +}; + +class Client { +public: + auto wake() -> Result; + + Client(Server&, TcpConnection connection) + : connection(connection) + { + } + +private: + TcpConnection connection; +}; + +} diff --git a/backend/src/tcp.cpp b/backend/src/tcp.cpp index 68dee7b..be3155d 100644 --- a/backend/src/tcp.cpp +++ b/backend/src/tcp.cpp @@ -1,50 +1,19 @@ #include "tcp.hpp" +#include "errno_shim.hpp" #include #include -#include #include -#include #include #include #include #include -#include #include -#include #include #include #include namespace mst { -auto event::make_listener_event(int fd) -> Event* -{ - auto ev = (Event*)malloc(sizeof(Event)); - - ev->variant = Listener; - ev->data.listener_fd = fd; - - return ev; -} - -auto event::make_connection_event(TcpConnection connection) -> Event* -{ - auto ev = (Event*)malloc(sizeof(Event)); - auto ptr = (TcpConnection*)std::malloc(sizeof(TcpConnection)); - *ptr = connection; - - ev->variant = Connection; - ev->data.connection = ptr; - - return ev; -} - -auto errno_shim(std::string_view message) -> std::string -{ - auto x = strerror(errno); - return std::format("{} ({})", message, x); -} - auto TcpConnection::write(uint8_t* buffer, size_t len) -> Result { ssize_t bytes_written = ::write(this->fd, buffer, len); @@ -86,61 +55,18 @@ auto TcpListener::bind(const std::string& host, uint16_t port) return std::unexpected(errno_shim("could not listen")); } - auto epoll_fd = ::epoll_create1(0); - - auto context = event::make_listener_event(socket_fd); - - auto event = epoll_event { .events = EPOLLIN, .data = { .ptr = context } }; - if (::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event) < 0) { - return std::unexpected(errno_shim("could not connect to epoll")); - } - - return TcpListener(epoll_fd, address); + return TcpListener(address, socket_fd); } -auto TcpListener::start() -> Result +auto TcpListener::accept() -> Result { - epoll_event events[128] = { }; - while (true) { - - auto events_len = ::epoll_wait(this->epoll_fd, events, 128, -1); - if (events_len < 0) { - return std::unexpected(errno_shim("could not poll")); - } - for (int i = 0; i < events_len; ++i) { - auto event = (event::Event*)events[i].data.ptr; - - switch (event->variant) { - case event::Listener: { - socklen_t size = sizeof(address); - int client = ::accept( - events[0].data.fd, (struct sockaddr*)&address, &size); - if (client < 0) { - return std::unexpected(errno_shim("could not accept")); - } - - auto context = event::make_connection_event( - TcpConnection(*this, client)); - - auto poll_event = epoll_event { .events = EPOLLIN, - .data = { .ptr = context } }; - - if (::epoll_ctl( - epoll_fd, EPOLL_CTL_ADD, client, &poll_event) - < 0) { - return std::unexpected( - errno_shim("could not add connection to epoll")); - } - - break; - } - case event::Connection: { - event->data.connection.wake(); - break; - } - } - } + socklen_t size = sizeof(this->address); + int client = ::accept(this->fd, (struct sockaddr*)&address, &size); + if (client < 0) { + return std::unexpected(errno_shim("could not accept")); } + + return TcpConnection(*this, client); } } diff --git a/backend/src/tcp.hpp b/backend/src/tcp.hpp index 30b6538..b1ce2ca 100644 --- a/backend/src/tcp.hpp +++ b/backend/src/tcp.hpp @@ -1,32 +1,13 @@ #pragma once +#include "result.hpp" #include -#include #include #include #include namespace mst { -template using Result = std::expected; - -class TcpConnection; - -namespace event { - enum Variant { Listener, Connection }; - typedef union { - int listener_fd; - TcpConnection* connection; - } Data; - struct Event { - Variant variant; - Data data; - }; - - auto make_listener_event(int fd) -> Event*; - auto make_connection_event(TcpConnection connection) -> Event*; -} - class TcpListener; class TcpConnection { @@ -36,9 +17,7 @@ public: TcpConnection(TcpListener&, int fd) : fd(fd) { }; -private: int fd; - event::Event event; }; class TcpListener { @@ -46,13 +25,13 @@ class TcpListener { public: static auto bind(const std::string& host, uint16_t port) -> Result; - auto start() -> Result; + auto accept() -> Result; + int fd; private: - TcpListener(int epoll_fd, sockaddr_in address) - : epoll_fd(epoll_fd) + TcpListener(sockaddr_in address, int fd) + : fd(fd) , address(address) { }; - int epoll_fd; sockaddr_in address; }; }