mirror of
https://github.com/Mercantec-GHC/h5-projekt-mst.git
synced 2026-08-27 05:37:39 +02:00
backend: poll/wake impl
This commit is contained in:
parent
bd09668dc1
commit
f81b0811a8
12
backend/src/errno_shim.hpp
Normal file
12
backend/src/errno_shim.hpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstring>
|
||||||
|
#include <format>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace mst {
|
||||||
|
auto inline errno_shim(std::string_view message) -> std::string
|
||||||
|
{
|
||||||
|
return std::format("{} ({})", message, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
53
backend/src/event_loop.cpp
Normal file
53
backend/src/event_loop.cpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include "event_loop.hpp"
|
||||||
|
#include "errno_shim.hpp"
|
||||||
|
#include "server.hpp"
|
||||||
|
#include <print>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
|
namespace mst::event {
|
||||||
|
|
||||||
|
auto Manager::start() -> Result<void>
|
||||||
|
{
|
||||||
|
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<Manager>
|
||||||
|
{
|
||||||
|
auto epoll_fd = ::epoll_create1(0);
|
||||||
|
if (epoll_fd < 0) {
|
||||||
|
std::unexpected(mst::errno_shim("could not create epoll"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Manager(epoll_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
backend/src/event_loop.hpp
Normal file
33
backend/src/event_loop.hpp
Normal file
@ -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 <typename Data> 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<void>;
|
||||||
|
int epoll_fd;
|
||||||
|
static auto create() -> Result<Manager>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Manager(int epoll_fd)
|
||||||
|
: epoll_fd(epoll_fd) { };
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
#include "tcp.hpp"
|
#include "event_loop.hpp"
|
||||||
|
#include "server.hpp"
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -9,15 +9,15 @@
|
|||||||
|
|
||||||
int main(void)
|
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) {
|
if (!x) {
|
||||||
std::println("{}", x.error());
|
std::println("{}", x.error());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
std::println("starting");
|
std::println("starting");
|
||||||
auto listener = x.value();
|
|
||||||
{
|
{
|
||||||
auto x = listener.loop();
|
auto x = mgr.start();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
5
backend/src/result.hpp
Normal file
5
backend/src/result.hpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <expected>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
template <typename T> using Result = std::expected<T, std::string>;
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
#include "server.hpp"
|
||||||
|
#include "errno_shim.hpp"
|
||||||
|
#include "event_loop.hpp"
|
||||||
|
#include <print>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
|
namespace mst {
|
||||||
|
|
||||||
|
auto Client::wake() -> Result<void>
|
||||||
|
{
|
||||||
|
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<void>
|
||||||
|
{
|
||||||
|
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<void>
|
||||||
|
{
|
||||||
|
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 { };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1 +1,36 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "event_loop.hpp"
|
||||||
|
#include "tcp.hpp"
|
||||||
|
|
||||||
|
namespace mst {
|
||||||
|
|
||||||
|
class Server {
|
||||||
|
public:
|
||||||
|
auto wake(mst::event::Manager& mgr) -> Result<void>;
|
||||||
|
static auto bind(mst::event::Manager& mgr, const std::string& host,
|
||||||
|
uint16_t port) -> Result<void>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TcpListener listener;
|
||||||
|
|
||||||
|
Server(TcpListener listener)
|
||||||
|
: listener(listener)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Client {
|
||||||
|
public:
|
||||||
|
auto wake() -> Result<void>;
|
||||||
|
|
||||||
|
Client(Server&, TcpConnection connection)
|
||||||
|
: connection(connection)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TcpConnection connection;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -1,50 +1,19 @@
|
|||||||
#include "tcp.hpp"
|
#include "tcp.hpp"
|
||||||
|
#include "errno_shim.hpp"
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <errno.h>
|
|
||||||
#include <expected>
|
#include <expected>
|
||||||
#include <format>
|
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
namespace mst {
|
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<size_t>
|
auto TcpConnection::write(uint8_t* buffer, size_t len) -> Result<size_t>
|
||||||
{
|
{
|
||||||
ssize_t bytes_written = ::write(this->fd, buffer, len);
|
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"));
|
return std::unexpected(errno_shim("could not listen"));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto epoll_fd = ::epoll_create1(0);
|
return TcpListener(address, socket_fd);
|
||||||
|
|
||||||
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);
|
auto TcpListener::accept() -> Result<TcpConnection>
|
||||||
}
|
|
||||||
|
|
||||||
auto TcpListener::start() -> Result<void>
|
|
||||||
{
|
{
|
||||||
epoll_event events[128] = { };
|
socklen_t size = sizeof(this->address);
|
||||||
while (true) {
|
int client = ::accept(this->fd, (struct sockaddr*)&address, &size);
|
||||||
|
|
||||||
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) {
|
if (client < 0) {
|
||||||
return std::unexpected(errno_shim("could not accept"));
|
return std::unexpected(errno_shim("could not accept"));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto context = event::make_connection_event(
|
return TcpConnection(*this, client);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "result.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <expected>
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
namespace mst {
|
namespace mst {
|
||||||
|
|
||||||
template <typename T> using Result = std::expected<T, std::string>;
|
|
||||||
|
|
||||||
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 TcpListener;
|
||||||
|
|
||||||
class TcpConnection {
|
class TcpConnection {
|
||||||
@ -36,9 +17,7 @@ public:
|
|||||||
TcpConnection(TcpListener&, int fd)
|
TcpConnection(TcpListener&, int fd)
|
||||||
: fd(fd) { };
|
: fd(fd) { };
|
||||||
|
|
||||||
private:
|
|
||||||
int fd;
|
int fd;
|
||||||
event::Event event;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TcpListener {
|
class TcpListener {
|
||||||
@ -46,13 +25,13 @@ class TcpListener {
|
|||||||
public:
|
public:
|
||||||
static auto bind(const std::string& host, uint16_t port)
|
static auto bind(const std::string& host, uint16_t port)
|
||||||
-> Result<TcpListener>;
|
-> Result<TcpListener>;
|
||||||
auto start() -> Result<void>;
|
auto accept() -> Result<TcpConnection>;
|
||||||
|
int fd;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TcpListener(int epoll_fd, sockaddr_in address)
|
TcpListener(sockaddr_in address, int fd)
|
||||||
: epoll_fd(epoll_fd)
|
: fd(fd)
|
||||||
, address(address) { };
|
, address(address) { };
|
||||||
int epoll_fd;
|
|
||||||
sockaddr_in address;
|
sockaddr_in address;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user