initial commit

This commit is contained in:
pantonshire committed 2026-05-25 06:49:18 +01:00
commit bb3f9a0bc6
18 files changed
+1053

No files matched your search

+22
View File
@@ -0,0 +1,22 @@
#include <stdlib.h>
#include "public/array_list.h"
int array_list_raw_alloc(struct array_list *al, size_t cap, size_t elem_size) {
void *buf;
if (!(buf = realloc(al->buf, cap * elem_size)))
return -1;
al->buf = buf;
al->cap = cap;
return 0;
}
void array_list_raw_cleanup(struct array_list *al) {
if (al->buf && al->cap) {
free(al->buf);
al->buf = NULL;
al->cap = 0;
al->len = 0;
}
}
+77
View File
@@ -0,0 +1,77 @@
#include "eventloop_platform.h"
#include "public/container_of.h"
#include "public/list.h"
int tlsl_eventloop_init(struct tlsl_eventloop *el) {
int platform_res;
intrusive_list_init_empty(&el->event_list);
if ((platform_res = tlsl_platform_el_init(el)))
return platform_res;
el->valid = true;
return 0;
}
void tlsl_eventloop_cleanup(struct tlsl_eventloop *el) {
struct intrusive_list *li;
struct tlsl_event *e;
if (!el || !el->valid)
return;
INTRUSIVE_LIST_FOR_EACH_NO_REMOVE(li, &el->event_list) {
e = TLSL_CONTAINER_OF(li, struct tlsl_event, list);
tlsl_platform_el_event_cleanup(el, e);
}
tlsl_platform_el_cleanup(el);
el->valid = false;
}
int tlsl_eventloop_wait(
struct tlsl_eventloop *el,
tlsl_event_id_t *id_out,
uint32_t *flags_out,
int timeout_ms)
{
return tlsl_platform_el_wait(el, id_out, flags_out, timeout_ms);
}
int tlsl_eventloop_clear(struct tlsl_eventloop *el, struct tlsl_event *e) {
return tlsl_platform_el_event_clear(el, e);
}
void tlsl_eventloop_remove(struct tlsl_eventloop *el, struct tlsl_event *e) {
tlsl_platform_el_event_remove(el, e);
tlsl_platform_el_event_cleanup(el, e);
}
int tlsl_eventloop_add_timer(
struct tlsl_eventloop *el,
struct tlsl_event *e,
uint64_t millis,
bool oneshot)
{
int res;
if ((res = tlsl_platform_el_add_timer(el, e, millis, oneshot)))
return res;
e->id.source = TLSL_EVENT_SOURCE_TIMER;
return 0;
}
int tlsl_eventloop_add_fd(
struct tlsl_eventloop *el,
struct tlsl_event *e,
int fd,
uint32_t flags)
{
int res;
if ((res = tlsl_platform_el_add_fd(el, e, fd, flags)))
return res;
e->id.source = TLSL_EVENT_SOURCE_FD;
return 0;
}
+191
View File
@@ -0,0 +1,191 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include "eventloop_platform.h"
#include "public/io.h"
struct tlsl_event_platform {
int epoll_registered_fd;
bool epoll_fd_owned;
};
struct tlsl_eventloop_platform {
int epoll_fd;
};
int tlsl_platform_el_init(struct tlsl_eventloop *el) {
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
if ((elp->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
return -1;
return 0;
}
void tlsl_platform_el_cleanup(struct tlsl_eventloop *el) {
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
close(elp->epoll_fd);
}
void tlsl_platform_el_event_remove(struct tlsl_eventloop *el, struct tlsl_event *e) {
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
TLSL_DEFINE_PLATFORM_EVENT_DATA(e, ep);
epoll_ctl(elp->epoll_fd, EPOLL_CTL_DEL, ep->epoll_registered_fd, NULL);
}
void tlsl_platform_el_event_cleanup(struct tlsl_eventloop *el, struct tlsl_event *e) {
TLSL_DEFINE_PLATFORM_EVENT_DATA(e, ep);
(void)el;
if (ep->epoll_fd_owned && (ep->epoll_registered_fd >= 0)) {
close(ep->epoll_registered_fd);
ep->epoll_registered_fd = -1;
}
}
static int clear_timerfd(int fd) {
uint64_t tfd_buf;
ssize_t res;
do {
res = read(fd, &tfd_buf, sizeof(tfd_buf));
} while ((res < 0) && (errno == EINTR));
if (res < 0)
return -1;
return 0;
}
int tlsl_platform_el_event_clear(struct tlsl_eventloop *el, struct tlsl_event *e) {
TLSL_DEFINE_PLATFORM_EVENT_DATA(e, ep);
int res;
(void)el;
if ((e->id.source == TLSL_EVENT_SOURCE_TIMER) && (ep->epoll_registered_fd >= 0)) {
if ((res = clear_timerfd(ep->epoll_registered_fd)))
return res;
}
return 0;
}
int tlsl_platform_el_wait(
struct tlsl_eventloop *el,
tlsl_event_id_t *id_out,
uint32_t *flags_out,
int timeout_ms)
{
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
struct epoll_event ev;
struct tlsl_event *e;
int res;
if (timeout_ms < 0)
timeout_ms = -1;
do {
res = epoll_pwait(elp->epoll_fd, &ev, 1, timeout_ms, NULL);
} while ((res < 0) && (errno == EINTR));
if (res <= 0) {
if (!res)
errno = ETIMEDOUT;
return -1;
}
e = (struct tlsl_event *)ev.data.ptr;
*id_out = e->id;
*flags_out = 0;
if (ev.events & EPOLLIN)
*flags_out |= TLSL_EVENT_READABLE;
if (ev.events & EPOLLOUT)
*flags_out |= TLSL_EVENT_WRITABLE;
return 0;
}
static int el_epoll_add(
struct tlsl_eventloop *el,
struct tlsl_event *e,
int fd,
enum tlsl_event_source source,
uint32_t events,
bool owned)
{
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
TLSL_DEFINE_PLATFORM_EVENT_DATA(e, ep);
struct epoll_event ev;
memset(&ev, 0, sizeof(ev));
ev.events = events;
ev.data.fd = fd;
ev.data.ptr = e;
if (epoll_ctl(elp->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
return -1;
e->id.obj = (uintptr_t)fd;
e->id.source = source;
ep->epoll_registered_fd = fd;
ep->epoll_fd_owned = owned;
return 0;
}
int tlsl_platform_el_add_timer(
struct tlsl_eventloop *el,
struct tlsl_event *e,
uint64_t millis,
bool oneshot)
{
FD_SCOPED(tfd);
struct itimerspec spec;
struct timespec ts;
int res;
ts.tv_sec = (long)(millis / 1000);
ts.tv_nsec = (long)((millis % 1000) * 1000000);
memset(&spec, 0, sizeof(spec));
spec.it_value = ts;
if (!oneshot)
spec.it_interval = ts;
if ((tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)) < 0)
return -1;
if (timerfd_settime(tfd, 0, &spec, NULL))
return -1;
if ((res = el_epoll_add(el, e, tfd, TLSL_EVENT_SOURCE_TIMER, EPOLLIN, true)))
return res;
FD_RELEASE(&tfd);
return 0;
}
int tlsl_platform_el_add_fd(
struct tlsl_eventloop *el,
struct tlsl_event *e,
int fd,
uint32_t flags)
{
uint32_t events;
events = 0;
if (flags & TLSL_EVENT_READABLE)
events |= EPOLLIN;
if (flags & TLSL_EVENT_WRITABLE)
events |= EPOLLOUT;
return el_epoll_add(el, e, fd, TLSL_EVENT_SOURCE_FD, events, false);
}
+242
View File
@@ -0,0 +1,242 @@
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include "eventloop_platform.h"
#include "public/io.h"
#define TLSL_EVENTLOOP_KQUEUE_FILTERS_MAX 2
struct tlsl_event_platform {
uintptr_t kqueue_ident;
uint16_t kqueue_filters[TLSL_EVENTLOOP_KQUEUE_FILTERS_MAX];
uint8_t kqueue_filters_len;
};
struct tlsl_eventloop_platform {
uintptr_t kqueue_timer_next;
int kqueue_fd;
};
struct el_kqueue_add_params {
uint16_t filter;
uint16_t flags;
int64_t data;
};
static int new_kqueue(void) {
#if defined(TLSL_PLATFORM_FREEBSD)
return kqueuex(KQUEUE_CLOEXEC);
#elif defined(TLSL_PLATFORM_OPENBSD) || defined(TLSL_PLATFORM_NETBSD)
return kqueue1(O_CLOEXEC);
#elif defined(TLSL_PLATFORM_DARWIN)
// Darwin / XNU sets cloexec by default:
// - https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/kern/kern_event.c#L3029
// - https://github.com/apple-oss-distributions/xnu/blob/f6217f891ac0bb64f3d375211650a4c1ff8ca1ea/bsd/kern/kern_event.c#L3070
return kqueue();
#else
#error unsupported platform for kqueue-based eventloop
#endif
}
int tlsl_platform_el_init(struct tlsl_eventloop *el) {
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
if ((elp->kqueue_fd = new_kqueue()) < 0)
return -1;
elp->kqueue_timer_next = 0;
return 0;
}
void tlsl_platform_el_cleanup(struct tlsl_eventloop *el) {
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
close(elp->kqueue_fd);
}
void tlsl_platform_el_event_remove(struct tlsl_eventloop *el, struct tlsl_event *e) {
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
TLSL_DEFINE_PLATFORM_EVENT_DATA(e, ep);
struct kevent kevs[TLSL_EVENTLOOP_KQUEUE_FILTERS_MAX];
unsigned i;
unsigned filters_len;
if ((filters_len = ep->kqueue_filters_len) > TLSL_EVENTLOOP_KQUEUE_FILTERS_MAX)
filters_len = TLSL_EVENTLOOP_KQUEUE_FILTERS_MAX;
if (!filters_len)
return;
memset(kevs, 0, sizeof(kevs));
for (i = 0; i < filters_len; i++) {
kevs[i].ident = ep->kqueue_ident;
kevs[i].filter = ep->kqueue_filters[i];
kevs[i].flags = EV_DELETE;
}
kevent(elp->kqueue_fd, kevs, filters_len, NULL, 0, NULL);
}
void tlsl_platform_el_event_cleanup(struct tlsl_eventloop *el, struct tlsl_event *e) {
(void)el;
(void)e;
}
int tlsl_platform_el_event_clear(struct tlsl_eventloop *el, struct tlsl_event *e) {
(void)el;
(void)e;
return 0;
}
int tlsl_platform_el_wait(
struct tlsl_eventloop *el,
tlsl_event_id_t *id_out,
uint32_t *flags_out,
int timeout_ms)
{
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
struct kevent kev;
struct tlsl_event *e;
struct timespec timeout;
struct timespec *timeout_ptr;
int res;
if (timeout_ms >= 0) {
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_nsec = (long)(timeout_ms % 1000) * 1000000;
timeout_ptr = &timeout;
}
else
timeout_ptr = NULL;
do {
res = kevent(elp->kqueue_fd, NULL, 0, &kev, 1, timeout_ptr);
} while ((res < 0) && (errno == EINTR));
if (res <= 0) {
if (!res)
errno = ETIMEDOUT;
return -1;
}
e = (struct tlsl_event *)kev.udata;
*id_out = e->id;
*flags_out = 0;
switch (kev.filter) {
case EVFILT_TIMER:
case EVFILT_READ:
*flags_out |= TLSL_EVENT_READABLE;
break;
case EVFILT_WRITE:
*flags_out |= TLSL_EVENT_WRITABLE;
break;
default:
break;
}
return 0;
}
static int el_kqueue_add(
struct tlsl_eventloop *el,
struct tlsl_event *e,
uintptr_t ident,
enum tlsl_event_source source,
const struct el_kqueue_add_params *params,
uint8_t params_len)
{
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
TLSL_DEFINE_PLATFORM_EVENT_DATA(e, ep);
struct kevent kevs[TLSL_EVENTLOOP_KQUEUE_FILTERS_MAX];
unsigned i;
if (!params_len || (params_len > TLSL_EVENTLOOP_KQUEUE_FILTERS_MAX)) {
errno = EINVAL;
return -1;
}
memset(kevs, 0, sizeof(kevs));
for (i = 0; i < params_len; i++) {
kevs[i].ident = ident;
kevs[i].filter = params[i].filter;
kevs[i].flags = EV_ADD | params[i].flags;
kevs[i].data = params[i].data;
kevs[i].udata = e;
}
if (kevent(elp->kqueue_fd, kevs, params_len, NULL, 0, NULL))
return -1;
e->id.obj = ident;
e->id.source = source;
ep->kqueue_ident = ident;
ep->kqueue_filters_len = params_len;
for (i = 0; i < params_len; i++)
ep->kqueue_filters[i] = params[i].filter;
return 0;
}
int tlsl_platform_el_add_timer(
struct tlsl_eventloop *el,
struct tlsl_event *e,
uint64_t millis,
bool oneshot)
{
TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp);
struct el_kqueue_add_params params;
int res;
params.filter = EVFILT_TIMER;
params.data = millis;
params.flags = 0;
if (oneshot)
params.flags |= EV_ONESHOT;
if ((res = el_kqueue_add(el, e, elp->kqueue_timer_next, TLSL_EVENT_SOURCE_TIMER, &params, 1)))
return res;
elp->kqueue_timer_next++;
return 0;
}
int tlsl_platform_el_add_fd(
struct tlsl_eventloop *el,
struct tlsl_event *e,
int fd,
uint32_t flags)
{
struct el_kqueue_add_params params[2];
uint8_t params_len;
params_len = 0;
if (flags & TLSL_EVENT_READABLE) {
params[params_len++] = (struct el_kqueue_add_params) {
.filter = EVFILT_READ,
.flags = 0,
.data = 0,
};
}
if (flags & TLSL_EVENT_WRITABLE) {
params[params_len++] = (struct el_kqueue_add_params) {
.filter = EVFILT_WRITE,
.flags = 0,
.data = 0,
};
}
return el_kqueue_add(el, e, (uintptr_t)fd, TLSL_EVENT_SOURCE_FD, params, params_len);
}
+18
View File
@@ -0,0 +1,18 @@
#include <stdlib.h>
#include <string.h>
#include "public/str.h"
str_buf_t sbuf_from_sview(str_view_t s) {
char *buf;
if (sview_is_null(s) || sview_is_empty(s))
return SBUF_NULL;
if (!(buf = malloc(s.len + 1)))
return SBUF_NULL;
memcpy(buf, s.ptr, s.len);
buf[s.len] = 0;
return (str_buf_t) { buf, s.len };
}