initial commit

main
pantonshire 4 weeks ago
commit bb3f9a0bc6

@ -0,0 +1,61 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <eventloop.h>
int main() {
struct tlsl_event timer, pipe_ev;
EVENTLOOP_STACK_SCOPED(el);
tlsl_event_id_t id;
uint32_t flags;
int pfds[2];
if (pipe(pfds))
return 1;
pid_t pid = fork();
if (pid < 0)
return 1;
if (!pid) {
char buf[] = "hello!";
sleep(5);
write(pfds[1], buf, sizeof(buf));
exit(0);
}
if (tlsl_eventloop_init(&el))
return 1;
if (tlsl_eventloop_add_timer(&el, &timer, 2000, false))
return 1;
if (tlsl_eventloop_add_fd(&el, &pipe_ev, pfds[0], TLSL_EVENT_READABLE))
return 1;
for (;;) {
if (tlsl_eventloop_wait(&el, &id, &flags, -1)) {
fprintf(stderr, "failed to wait");
continue;
}
if (tlsl_event_id_eq(id, timer.id)) {
tlsl_eventloop_clear(&el, &timer);
printf("timer tick flags=%u\n", flags);
}
else if (tlsl_event_id_eq(id, pipe_ev.id)) {
char buf[1024] = { 0 };
read(pfds[0], buf, sizeof(buf) - 1);
printf("pipe event flags=%u buf=%s\n", flags, buf);
break;
}
else {
fprintf(stderr, "unknown id");
}
}
tlsl_eventloop_remove(&el, &timer);
tlsl_eventloop_remove(&el, &pipe_ev);
return 0;
}

@ -0,0 +1,42 @@
#pragma once
#include "public/typecheck.h"
#include "public/eventloop.h"
#define TLSL_PLATFORM_EVENT_DATA(EVENT) ({ \
TLSL_CHECK_TYPE(struct tlsl_event *, EVENT); \
TLSL_CHECK_ARRAY((EVENT)->_platform); \
_Static_assert(sizeof((EVENT)->_platform) >= sizeof(struct tlsl_event_platform), \
"event platform buf too small"); \
_Static_assert(__alignof__((EVENT)->_platform) >= __alignof__(struct tlsl_event_platform), \
"event platform buf alignment too small"); \
(struct tlsl_event_platform *)(EVENT->_platform); \
})
#define TLSL_DEFINE_PLATFORM_EVENT_DATA(EVENT, NAME) \
struct tlsl_event_platform *NAME = TLSL_PLATFORM_EVENT_DATA(EVENT)
#define TLSL_PLATFORM_EVENTLOOP_DATA(EL) ({ \
TLSL_CHECK_TYPE(struct tlsl_eventloop *, EL); \
TLSL_CHECK_ARRAY((EL)->_platform); \
_Static_assert(sizeof((EL)->_platform) >= sizeof(struct tlsl_eventloop_platform), \
"eventloop platform buf too small"); \
_Static_assert(__alignof__((EL)->_platform) >= __alignof__(struct tlsl_eventloop_platform), \
"eventloop platform buf alignment too small"); \
(struct tlsl_eventloop_platform *)(EL->_platform); \
})
#define TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(EL, NAME) \
struct tlsl_eventloop_platform *NAME = TLSL_PLATFORM_EVENTLOOP_DATA(EL)
struct tlsl_event_platform;
struct tlsl_eventloop_platform;
int tlsl_platform_el_init(struct tlsl_eventloop *el);
void tlsl_platform_el_cleanup(struct tlsl_eventloop *el);
void tlsl_platform_el_event_remove(struct tlsl_eventloop *el, struct tlsl_event *e);
void tlsl_platform_el_event_cleanup(struct tlsl_eventloop *el, struct tlsl_event *e);
int tlsl_platform_el_event_clear(struct tlsl_eventloop *el, struct tlsl_event *e);
int tlsl_platform_el_wait(struct tlsl_eventloop *el, tlsl_event_id_t *id_out, uint32_t *flags_out, int timeout_ms);
int tlsl_platform_el_add_timer(struct tlsl_eventloop *el, struct tlsl_event *e, uint64_t millis, bool oneshot);
int tlsl_platform_el_add_fd(struct tlsl_eventloop *el, struct tlsl_event *e, int fd, uint32_t flags);

@ -0,0 +1,54 @@
#pragma once
#include <stdint.h>
#include "scoped.h"
#define ARRAY_LIST_EMPTY ((struct array_list) { NULL, 0, 0 })
struct array_list {
void *buf;
size_t len;
size_t cap;
};
int array_list_raw_alloc(struct array_list *al, size_t cap, size_t elem_size);
void array_list_raw_cleanup(struct array_list *al);
#define DEF_ARRAY_LIST_TYPE(NAME, TYPE, UNOCCUPIED, CLEANUP_FN) \
static inline TYPE *array_list_##NAME##_get(struct array_list *al, size_t i) { \
if (i >= al->len) \
return NULL; \
return &((TYPE *)al->buf)[i]; \
} \
static inline const TYPE *array_list_##NAME##_cget(const struct array_list *al, size_t i) { \
if (i >= al->len) \
return NULL; \
return &((const TYPE *)al->buf)[i]; \
} \
static inline void array_list_##NAME##_cleanup(struct array_list *al) { \
size_t i; \
for (i = 0; i < al->len; i++) \
CLEANUP_FN(array_list_##NAME##_get(al, i)); \
array_list_raw_cleanup(al); \
} \
static inline int array_list_##NAME##_alloc(struct array_list *al, size_t cap) { \
size_t i; \
if (cap < al->len) { \
for (i = cap; i < al->len; i++) \
CLEANUP_FN(array_list_##NAME##_get(al, i)); \
al->len = cap; \
} \
return array_list_raw_alloc(al, cap, sizeof(TYPE)); \
} \
static inline int array_list_##NAME##_push_noalloc_byval(struct array_list *al, TYPE elem) { \
if (al->len >= al->cap) \
return -1; \
((TYPE *)al->buf)[al->len++] = elem; \
return 0; \
} \
DEF_SCOPED_TYPE( \
array_list_##NAME, \
struct array_list, \
ARRAY_LIST_EMPTY, \
array_list_##NAME##_cleanup)

@ -0,0 +1,10 @@
#pragma once
#include <stddef.h>
#include "typecheck.h"
#define TLSL_CONTAINER_OF(PTR, TYPE, MEMBER) ({ \
TLSL_TYPEOFS_EQ(((TYPE *)NULL)->MEMBER, *(PTR)); \
(TYPE *)((char *)(PTR) - offsetof(TYPE, MEMBER)); \
})

@ -0,0 +1,51 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include "list.h"
#include "scoped.h"
#define TLSL_EVENT_READABLE (1 << 0)
#define TLSL_EVENT_WRITABLE (1 << 1)
enum tlsl_event_source {
TLSL_EVENT_SOURCE_FD,
TLSL_EVENT_SOURCE_TIMER,
};
typedef struct {
uintptr_t obj;
enum tlsl_event_source source;
} tlsl_event_id_t;
struct tlsl_event {
tlsl_event_id_t id;
struct intrusive_list list;
char _platform[16] __attribute__((aligned));
};
struct tlsl_eventloop {
struct intrusive_list event_list;
bool valid;
char _platform[16] __attribute__((aligned));
};
static inline bool tlsl_event_id_eq(tlsl_event_id_t eid1, tlsl_event_id_t eid2) {
return (eid1.obj == eid2.obj) && (eid1.source == eid2.source);
}
int tlsl_eventloop_init(struct tlsl_eventloop *el);
void tlsl_eventloop_cleanup(struct tlsl_eventloop *el);
int tlsl_eventloop_wait(struct tlsl_eventloop *el, tlsl_event_id_t *id_out, uint32_t *flags_out, int timeout_ms);
int tlsl_eventloop_clear(struct tlsl_eventloop *el, struct tlsl_event *e);
void tlsl_eventloop_remove(struct tlsl_eventloop *el, struct tlsl_event *e);
int tlsl_eventloop_add_timer(struct tlsl_eventloop *el, struct tlsl_event *e, uint64_t millis, bool oneshot);
int tlsl_eventloop_add_fd(struct tlsl_eventloop *el, struct tlsl_event *e, int fd, uint32_t flags);
DEF_SCOPED_TYPE(eventloop_stack, struct tlsl_eventloop, (struct tlsl_eventloop) { .valid = false }, tlsl_eventloop_cleanup)
#define EVENTLOOP_STACK_SCOPED(VAL) SCOPED(eventloop_stack, struct tlsl_eventloop, VAL)
#define EVENTLOOP_STACK_CLOSE(EL) _scoped_eventloop_stack_cleanup(EL)

@ -0,0 +1,21 @@
#pragma once
#include <unistd.h>
#include <sys/socket.h>
#include "scoped.h"
static inline void _tlsl_close_if_nonnegative(int *fd) {
if (*fd >= 0)
close(*fd);
}
DEF_SCOPED_TYPE(fd, int, -1, _tlsl_close_if_nonnegative)
#define FD_SCOPED(VAL) SCOPED(fd, int, VAL)
#define FD_CLOSE(FD) _tlsl_scoped_fd_cleanup(FD)
#define FD_RELEASE(FD) _tlsl_scoped_fd_release(FD)
static inline int setsockopt_int(int fd, int level, int option, int val) {
return setsockopt(fd, level, option, &val, sizeof(val));
}

@ -0,0 +1,46 @@
#pragma once
#include "typecheck.h"
struct intrusive_list {
struct intrusive_list *next;
struct intrusive_list *prev;
};
static inline void intrusive_list_init_empty(struct intrusive_list *head) {
head->next = head;
head->prev = head;
}
static inline void intrusive_list_del(struct intrusive_list *node) {
struct intrusive_list old_node;
old_node = *node;
node->prev->next = old_node.next;
node->next->prev = old_node.prev;
}
static inline void intrusive_list_add(
struct intrusive_list *add_after,
struct intrusive_list *new_node)
{
new_node->prev = add_after;
new_node->next = add_after->next;
new_node->prev->next = new_node;
new_node->next->prev = new_node;
}
#define INTRUSIVE_LIST_FOR_EACH(ITER, NEXT, HEAD) \
for ( \
(void)TLSL_CHECK_TYPE(struct intrusive_list *, HEAD), \
(ITER) = (HEAD)->next, \
(NEXT) = (ITER)->next; \
(ITER) != (HEAD); \
(ITER) = (NEXT), (NEXT) = (ITER)->next)
#define INTRUSIVE_LIST_FOR_EACH_NO_REMOVE(ITER, HEAD) \
for ( \
(void)TLSL_CHECK_TYPE(struct intrusive_list *, HEAD), \
(ITER) = (HEAD)->next; \
(ITER) != (HEAD); \
(ITER) = (ITER)->next)

@ -0,0 +1,16 @@
#pragma once
#include <stdlib.h>
#include "scoped.h"
#include "typecheck.h"
static inline void _tlsl_mem_free(void **p) {
free(*p);
}
DEF_SCOPED_TYPE(mem, void *, NULL, _tlsl_mem_free)
#define MEM_SCOPED(TYPE, VAL) SCOPED(mem, TYPE *, VAL)
#define MEM_FREE(TYPE, MEM) ({ TLSL_CHECK_TYPE(TYPE **, MEM), (TYPE *)_scoped_mem_cleanup((void **)(MEM)); })
#define MEM_RELEASE(TYPE, MEM) ({ TLSL_CHECK_TYPE(TYPE **, MEM), (TYPE *)_scoped_mem_release((void **)(MEM)); })

@ -0,0 +1,22 @@
#pragma once
#define DEF_SCOPED_TYPE(NAME, TYPE, UNOCCUPIED, CLEANUP_FN) \
static inline TYPE _tlsl_scoped_##NAME##_unoccupied(void) { \
return (UNOCCUPIED); \
} \
static inline void _tlsl_scoped_##NAME##_cleanup(TYPE *_val) { \
CLEANUP_FN(_val); \
*_val = (UNOCCUPIED); \
} \
static inline void _tlsl_scoped_##NAME##_cleanup_raw(void *_val) { \
_tlsl_scoped_##NAME##_cleanup((TYPE *)_val); \
} \
static inline TYPE _tlsl_scoped_##NAME##_release(TYPE *_val) { \
TYPE _released = *_val; \
*_val = (UNOCCUPIED); \
return _released; \
}
#define SCOPED(NAME, TYPE, VAL) \
TYPE VAL __attribute__((cleanup(_tlsl_scoped_##NAME##_cleanup_raw))) \
= _tlsl_scoped_##NAME##_unoccupied()

@ -0,0 +1,88 @@
#pragma once
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#define SVIEW_LIT(S) ((void)(S "LITERAL_CHECK"), (str_view_t) { S, sizeof(S) - 1 })
#define SVIEW_NULL ((str_view_t) { NULL, 0 })
#define SBUF_NULL ((str_buf_t) { NULL, 0 })
#define SBUF_SCOPED(NAME) str_buf_t NAME __attribute__((cleanup(sb_free))) = SB_NULL
#define SVIEW_PRI "%.*s"
#define SVIEW_PRI_ARGS(S) (int)((S).len), (S).ptr
// String which is:
// - Not owned
// - Not necessarily heap-allocated
// - Not necessarily nul-terminated
typedef struct {
const char *ptr;
size_t len;
} str_view_t;
// String which is:
// - Owned
// - Heap-allocated
// - Nul-terminated
// The length does not include the nul terminator.
typedef struct {
char *ptr;
size_t len;
} str_buf_t;
static inline bool sview_is_null(str_view_t s) {
return !s.ptr;
}
static inline bool sview_is_empty(str_view_t s) {
return !s.len;
}
static inline bool sbuf_is_null(str_buf_t buf) {
return !buf.ptr;
}
static inline bool sbuf_is_empty(str_buf_t buf) {
return !buf.len;
}
static inline str_view_t sview_from_parts(const char *ptr, size_t len) {
return (str_view_t) { ptr, len };
}
static inline str_view_t sview_from_cstr(const char *ptr, size_t maxlen) {
size_t len;
len = strnlen(ptr, maxlen);
return sview_from_parts(ptr, len);
}
static inline str_view_t sview_from_cstr_unbounded(const char *ptr) {
size_t len = strlen(ptr);
return sview_from_parts(ptr, len);
}
static inline bool sview_eq(str_view_t s1, str_view_t s2) {
return (s1.len == s2.len) && ((!s1.ptr || !s2.ptr)
? (s1.ptr == s2.ptr)
: !memcmp(s1.ptr, s2.ptr, s1.len));
}
static inline str_view_t sview_substr(str_view_t s, size_t start, size_t end) {
if ((start > end) || (start > s.len) || (end > s.len))
return SVIEW_NULL;
return sview_from_parts(s.ptr + start, end - start);
}
static inline str_view_t sbuf_view(str_buf_t buf) {
return sview_from_parts(buf.ptr, buf.len);
}
str_buf_t sbuf_from_sview(str_view_t s);
static inline void sbuf_free(str_buf_t *buf) {
free(buf->ptr);
buf->ptr = NULL;
buf->len = 0;
}

@ -0,0 +1,17 @@
#pragma once
#define TLSL_TYPES_EQ(TYPE_LHS, TYPE_RHS) ({ \
_Static_assert(__builtin_types_compatible_p(TYPE_LHS, TYPE_RHS), \
"types mismatch"); \
1; \
})
#define TLSL_TYPEOFS_EQ(VAL_LHS, VAL_RHS) TLSL_TYPES_EQ(typeof(VAL_LHS), typeof(VAL_RHS))
#define TLSL_CHECK_TYPE(TYPE, VAL) TLSL_TYPES_EQ(TYPE, typeof(VAL))
#define TLSL_CHECK_ARRAY(VAL) ({ \
_Static_assert(!__builtin_types_compatible_p(typeof(VAL), typeof(&(VAL)[0])), \
"not an array"); \
1; \
})

@ -0,0 +1,74 @@
project(
'tlsl',
'c',
default_options : ['c_std=gnu11,c11'],
meson_version: '>=1.1'
)
eventloop_epoll = 'epoll'
eventloop_kqueue = 'kqueue'
platform_info = {
'linux': {
'eventloop': eventloop_epoll,
},
'freebsd': {
'eventloop': eventloop_kqueue,
},
'openbsd': {
'eventloop': eventloop_kqueue,
},
'netbsd': {
'eventloop': eventloop_kqueue,
},
'darwin': {
'eventloop': eventloop_kqueue,
},
}[target_machine.system()]
eventloop_info = {
eventloop_epoll: {
'path': 'src/eventloop_epoll.c',
},
eventloop_kqueue: {
'path': 'src/eventloop_kqueue.c',
},
}[platform_info['eventloop']]
sources = [
'src/str.c',
'src/eventloop.c',
'src/array_list.c',
eventloop_info['path'],
]
includes = include_directories('include')
public_includes = include_directories('include/public')
args = [
'-Wall',
'-DTLSL_PLATFORM_' + target_machine.system().to_upper(),
'-DTLSL_EVENTLOOP_' + platform_info['eventloop'].to_upper(),
]
tlsl_lib = shared_library(
meson.project_name(),
sources,
include_directories: includes,
c_args: args,
)
tlsl_dep = declare_dependency(
include_directories: public_includes,
link_with: tlsl_lib,
)
if get_option('examples')
executable(
'example_eventloop',
[ 'examples/eventloop.c' ],
dependencies: tlsl_dep,
install: false,
)
endif

@ -0,0 +1 @@
option('examples', type: 'boolean', value: true, description: 'Build example binaries')

@ -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, &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);
}

@ -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 };
}
Loading…
Cancel
Save