initial commit
This commit is contained in:
commit
bb3f9a0bc6
18 files changed
+1053
No files matched your search
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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, ¶ms, 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);
|
||||
}
|
||||
@@ -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 };
|
||||
}
|
||||
Reference in new issue
Block a user