feat(lnm): start of event loop
ci/woodpecker/push/build Pipeline was successful Details

lnm
Jef Roosens 2023-11-21 22:39:59 +01:00
parent 20b6b593eb
commit 8a3be2b07c
Signed by: Jef Roosens
GPG Key ID: 02D4C0997E74717B
4 changed files with 147 additions and 1 deletions

View File

@ -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

View File

@ -1,11 +1,13 @@
#ifndef LNM_LOOP
#define LNM_LOOP
#include <stdint.h>
#include <stdlib.h>
#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

View File

@ -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);

View File

@ -1 +1,134 @@
#include "lnm/loop.h"
#include <netinet/in.h>
#include <poll.h>
#include <unistd.h>
#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;
}