event loop for darwin
parent
45284910cc
commit
9a00a29af6
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "typecheck.h"
|
||||
|
||||
#define CONTAINER_OF(PTR, TYPE, MEMBER) ({ \
|
||||
CHECK_TYPE(__typeof__(((TYPE *)NULL)->MEMBER), *(PTR)); \
|
||||
(TYPE *)((char *)(PTR) - offsetof(TYPE, MEMBER)); \
|
||||
})
|
||||
@ -1,6 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
struct eventloop;
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
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);
|
||||
|
||||
@ -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)
|
||||
@ -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; \
|
||||
})
|
||||
@ -1,28 +1,121 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue