diff --git a/include/container_of.h b/include/container_of.h new file mode 100644 index 0000000..dad39c2 --- /dev/null +++ b/include/container_of.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include "typecheck.h" + +#define CONTAINER_OF(PTR, TYPE, MEMBER) ({ \ + CHECK_TYPE(__typeof__(((TYPE *)NULL)->MEMBER), *(PTR)); \ + (TYPE *)((char *)(PTR) - offsetof(TYPE, MEMBER)); \ +}) diff --git a/include/eventloop.h b/include/eventloop.h index 220f27b..fbad995 100644 --- a/include/eventloop.h +++ b/include/eventloop.h @@ -1,6 +1,47 @@ #pragma once -struct eventloop; +#include +#include +#include -struct eventloop *eventloop_new(void); -void eventloop_free(struct eventloop *el); +#include "list.h" + +#define OWD_EVENT_READABLE (1 << 0) +#define OWD_EVENT_WRITABLE (1 << 1) + +enum owd_event_source { + OWD_EVENT_SOURCE_FD, + OWD_EVENT_SOURCE_TIMER, +}; + +typedef struct { + uintptr_t obj; + enum owd_event_source source; +} owd_event_id_t; + +struct owd_event { + owd_event_id_t id; + struct intrusive_list list; + + uintptr_t kqueue_ident; + short kqueue_filter; +}; + +struct owd_eventloop { + struct intrusive_list event_list; + + uintptr_t kqueue_timer_next; + int kqueue_fd; +}; + +static inline bool owd_event_id_eq(owd_event_id_t eid1, owd_event_id_t eid2) { + return (eid1.obj == eid2.obj) && (eid1.source == eid2.source); +} + +int owd_eventloop_init(struct owd_eventloop *el); +void owd_eventloop_cleanup(struct owd_eventloop *el); + +int owd_eventloop_wait(struct owd_eventloop *el, owd_event_id_t *id_out, const struct timespec *timeout); +int owd_eventloop_clear(struct owd_eventloop *el, struct owd_event *e); +void owd_eventloop_remove(struct owd_eventloop *el, struct owd_event *e); +int owd_eventloop_add_timer(struct owd_eventloop *el, struct owd_event *e, uint64_t millis, bool oneshot); diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..e432146 --- /dev/null +++ b/include/list.h @@ -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 ( \ + 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 ( \ + CHECK_TYPE(struct intrusive_list *, HEAD), \ + (ITER) = (HEAD)->next; \ + (ITER) != (HEAD); \ + (ITER) = (ITER)->next) diff --git a/include/mem.h b/include/mem.h index 3d70c45..8d8c7ba 100644 --- a/include/mem.h +++ b/include/mem.h @@ -3,6 +3,7 @@ #include #include "scoped.h" +#include "typecheck.h" DEF_SCOPED_TYPE(mem, void *, NULL, free) diff --git a/include/scoped.h b/include/scoped.h index f6812c1..6899bd2 100644 --- a/include/scoped.h +++ b/include/scoped.h @@ -1,13 +1,5 @@ #pragma once -#define CHECK_TYPE(TYPE, VAL) ({ \ - typedef void (*_check_type_dummy_fn_t)(TYPE); \ - _check_type_dummy_fn_t _check_type_dummy_fn = (_check_type_dummy_fn_t)NULL; \ - if (0) \ - _check_type_dummy_fn(VAL); \ - 1; \ -}) - #define DEF_SCOPED_TYPE(NAME, TYPE, UNOCCUPIED, CLEANUP_FN) \ static inline TYPE _scoped_##NAME##_unoccupied(void) { \ return (UNOCCUPIED); \ diff --git a/include/typecheck.h b/include/typecheck.h new file mode 100644 index 0000000..24a2189 --- /dev/null +++ b/include/typecheck.h @@ -0,0 +1,9 @@ +#pragma once + +#define CHECK_TYPE(TYPE, VAL) ({ \ + typedef void (*_check_type_dummy_fn_t)(TYPE); \ + _check_type_dummy_fn_t _check_type_dummy_fn = (_check_type_dummy_fn_t)NULL; \ + if (0) \ + _check_type_dummy_fn(VAL); \ + 1; \ +}) diff --git a/src/eventloop.c b/src/eventloop.c index 621dd1e..db775c0 100644 --- a/src/eventloop.c +++ b/src/eventloop.c @@ -1,28 +1,121 @@ -#include #include +#include #include +#include +#include +#include #include "eventloop.h" -#include "mem.h" +#include "container_of.h" +#include "list.h" -struct eventloop { - int kqueue_fd; -}; +int owd_eventloop_init(struct owd_eventloop *el) { + intrusive_list_init_empty(&el->event_list); -struct eventloop *eventloop_new(void) { - MEM_SCOPED(struct eventloop, el); + if ((el->kqueue_fd = kqueue()) < 0) + return -1; + el->kqueue_timer_next = 0; - if (!(el = malloc(sizeof(struct eventloop)))) { - errno = ENOMEM; - return NULL; - } + return 0; +} + +static void event_cleanup(struct owd_eventloop *el, struct owd_event *e) { + if (!e) + return; - return MEM_RELEASE(struct eventloop, &el); + // TODO: any platform-specific cleanup } -void eventloop_free(struct eventloop *el) { +void owd_eventloop_cleanup(struct owd_eventloop *el) { + struct intrusive_list *li; + struct owd_event *e; + if (!el) return; + + INTRUSIVE_LIST_FOR_EACH_NO_REMOVE(li, &el->event_list) { + e = CONTAINER_OF(li, struct owd_event, list); + event_cleanup(el, e); + } + close(el->kqueue_fd); - free(el); +} + +int owd_eventloop_wait( + struct owd_eventloop *el, + owd_event_id_t *id_out, + const struct timespec *timeout) +{ + struct kevent kev; + int res; + + do { + res = kevent(el->kqueue_fd, NULL, 0, &kev, 1, timeout); + } while ((res < 0) && (errno == EINTR)); + + if (res <= 0) { + if (!res) + errno = ETIMEDOUT; + return -1; + } + + id_out->obj = kev.ident; + + if (kev.filter & EVFILT_TIMER) + id_out->source = OWD_EVENT_SOURCE_TIMER; + else + id_out->source = OWD_EVENT_SOURCE_FD; + + return 0; +} + +int owd_eventloop_clear(struct owd_eventloop *el, struct owd_event *e) { + // TODO: read timerfd on linux + return 0; +} + +void owd_eventloop_remove(struct owd_eventloop *el, struct owd_event *e) { + struct kevent kev; + + memset(&kev, 0, sizeof(kev)); + kev.ident = e->kqueue_ident; + kev.filter = e->kqueue_filter; + kev.flags = EV_DELETE; + kevent(el->kqueue_fd, &kev, 1, NULL, 0, NULL); + + event_cleanup(el, e); +} + +int owd_eventloop_add_timer( + struct owd_eventloop *el, + struct owd_event *e, + uint64_t millis, + bool oneshot) +{ + struct kevent kev; + uintptr_t ident; + unsigned short kev_flags; + + ident = el->kqueue_timer_next; + + kev_flags = 0; + if (oneshot) + kev_flags |= EV_ONESHOT; + + memset(&kev, 0, sizeof(kev)); + kev.ident = ident; + kev.filter = EVFILT_TIMER; + kev.flags = EV_ADD | kev_flags; + kev.data = (int64_t)millis; + + if (kevent(el->kqueue_fd, &kev, 1, NULL, 0, NULL)) + return -1; + + e->id.obj = ident; + e->id.source = OWD_EVENT_SOURCE_TIMER; + e->kqueue_ident = kev.ident; + e->kqueue_filter = kev.filter; + + el->kqueue_timer_next++; + return 0; }