From 8a3be2b07c4242e3a21bfb98ad6a0bff216a5584 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Tue, 21 Nov 2023 22:39:59 +0100 Subject: [PATCH] feat(lnm): start of event loop --- lnm/include/lnm/common.h | 2 + lnm/include/lnm/loop.h | 9 ++ lnm/src/_include/lnm/loop_internal.h | 2 + lnm/src/loop/lnm_loop.c | 135 ++++++++++++++++++++++++++- 4 files changed, 147 insertions(+), 1 deletion(-) diff --git a/lnm/include/lnm/common.h b/lnm/include/lnm/common.h index 658695a..26a3932 100644 --- a/lnm/include/lnm/common.h +++ b/lnm/include/lnm/common.h @@ -20,6 +20,8 @@ typedef enum { lnm_err_ok = 0, lnm_err_failed_alloc, + lnm_err_failed_network, + lnm_err_failed_poll, } lnm_err; #endif diff --git a/lnm/include/lnm/loop.h b/lnm/include/lnm/loop.h index f292ae8..c0bce0b 100644 --- a/lnm/include/lnm/loop.h +++ b/lnm/include/lnm/loop.h @@ -1,11 +1,13 @@ #ifndef LNM_LOOP #define LNM_LOOP +#include #include #include "lnm/common.h" #define LNM_LOOP_BUF_SIZE 4096 +#define LNM_LOOP_INITIAL_CONNS 16 typedef enum { lnm_loop_state_req = 0, @@ -33,10 +35,17 @@ typedef struct { struct { lnm_loop_conn **arr; size_t len; + size_t open; } conns; void *gctx; lnm_err (*ctx_init)(void **out, void *gctx); void (*ctx_free)(void *ctx); } lnm_loop; +lnm_err lnm_loop_init(lnm_loop **out, void *gctx, + lnm_err (*ctx_init)(void **out, void *gctx), + void (*ctx_free)(void *ctx)); + +lnm_err lnm_loop_run(lnm_loop *l, uint16_t port); + #endif diff --git a/lnm/src/_include/lnm/loop_internal.h b/lnm/src/_include/lnm/loop_internal.h index afbb386..0c91535 100644 --- a/lnm/src/_include/lnm/loop_internal.h +++ b/lnm/src/_include/lnm/loop_internal.h @@ -3,3 +3,5 @@ lnm_err lnm_loop_conn_init(lnm_loop_conn **out, lnm_loop *l); void lnm_loop_conn_free(lnm_loop *l, lnm_loop_conn *conn); + +lnm_err lnm_loop_accept(lnm_loop *l); diff --git a/lnm/src/loop/lnm_loop.c b/lnm/src/loop/lnm_loop.c index ef96614..d835824 100644 --- a/lnm/src/loop/lnm_loop.c +++ b/lnm/src/loop/lnm_loop.c @@ -1 +1,134 @@ -#include "lnm/loop.h" +#include +#include +#include + +#include "lnm/common.h" +#include "lnm/loop_internal.h" + +lnm_err lnm_loop_init(lnm_loop **out, void *gctx, + lnm_err (*ctx_init)(void **out, void *gctx), + void (*ctx_free)(void *ctx)) { + lnm_loop *l = calloc(1, sizeof(lnm_loop)); + + if (l == NULL) { + return lnm_err_failed_alloc; + } + + l->gctx = gctx; + l->ctx_init = ctx_init; + l->ctx_free = ctx_free; + + *out = l; + + return lnm_err_ok; +} + +lnm_err lnm_loop_run(lnm_loop *l, uint16_t port) { + int listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + + if (listen_fd < 0) { + return lnm_err_failed_network; + } + + int val = 1; + int res = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)); + + if (res < 0) { + return lnm_err_failed_network; + } + + struct sockaddr_in addr = {.sin_family = AF_INET, + .sin_port = ntohs(port), + .sin_addr.s_addr = ntohl(0)}; + + res = bind(listen_fd, (const struct sockaddr *)&addr, sizeof(addr)); + + if (res < 0) { + return lnm_err_failed_network; + } + + res = listen(listen_fd, SOMAXCONN); + + if (res < 0) { + return lnm_err_failed_network; + } + + struct pollfd *poll_args = + malloc((LNM_LOOP_INITIAL_CONNS + 1) * sizeof(struct pollfd)); + size_t poll_args_cap = LNM_LOOP_INITIAL_CONNS + 1; + + if (poll_args == NULL) { + return lnm_err_failed_alloc; + } + + // First argument is listening socket + poll_args[0].fd = listen_fd; + poll_args[0].events = POLLIN; + + while (1) { + size_t poll_args_len = 1; + + // Add all open connections to the poll command + for (size_t i = 0; i < l->conns.len && poll_args_len <= l->conns.open; + i++) { + lnm_loop_conn *conn = l->conns.arr[i]; + + if (conn == NULL) { + continue; + } + + poll_args[poll_args_len].fd = conn->fd; + poll_args[poll_args_len].events = + ((conn->state == lnm_loop_state_req) ? POLLIN : POLLOUT) | POLLERR; + + poll_args_len++; + } + + int polled = poll(poll_args, poll_args_len, -1); + + if (polled < 0) { + return lnm_err_failed_poll; + } + + if (poll_args[0].revents) { + lnm_loop_accept(l); + polled--; + } + + for (size_t i = 1; i < poll_args_len && polled > 0; i++) { + if (poll_args[i].revents) { + lnm_loop_conn *conn = l->conns.arr[poll_args[i].fd]; + + // TODO actual IO + + if (conn->state == lnm_loop_state_end) { + l->conns.arr[conn->fd] = NULL; + close(conn->fd); + + l->conns.open--; + lnm_loop_conn_free(l, conn); + } + + polled--; + } + } + + if (poll_args_cap < l->conns.open + 1) { + struct pollfd *buf = malloc((l->conns.open + 1) * sizeof(struct pollfd)); + + if (buf == NULL) { + return lnm_err_failed_alloc; + } + + buf[0].fd = listen_fd; + buf[0].events = POLLIN; + + free(poll_args); + poll_args = buf; + + poll_args_cap = l->conns.open + 1; + } + } + + return lnm_err_ok; +}