initial commit
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, ¶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 };
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue