Something went wrong. Try again.
extremely opinionated bar. you don't want to use this
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370#include "bar/event.h"
#include <errno.h>#include <signal.h>#include <stddef.h>#include <stdlib.h>#include <unistd.h>
#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && \ !defined(__NetBSD__) && !defined(__DragonFly__)#error "no epoll or kqueue support detected"#endif
#ifdef __linux__#include <sys/epoll.h>#include <sys/timerfd.h>#else#include <sys/event.h>#include <sys/time.h>#endif
typedef struct { int fd; uint32_t events; io_callback_t fn; void *data;} io_handler_t;
typedef struct { uint64_t interval_ms; uint64_t expire_at; bool repeat; bool used; timer_callback_t fn; void *data;} timer_handler_t;
static io_handler_t *io_handlers = nullptr;static size_t io_len = 0;static size_t io_cap = 0;
static timer_handler_t *tm_handlers = nullptr;static size_t tm_len = 0;static size_t tm_cap = 0;
static int poll_fd = -1;static volatile sig_atomic_t running = false;
static idle_callback_t idle_fn = nullptr;static void *idle_data = nullptr;
static io_handler_t *alloc_io(void){ for (size_t i = 0; i < io_len; i++) { if (io_handlers[i].fd < 0) return &io_handlers[i]; } if (io_len >= io_cap) { size_t new_cap = io_cap ? io_cap * 2 : 4; io_handler_t *new_arr = realloc(io_handlers, new_cap * sizeof *new_arr); if (!new_arr) return nullptr; io_handlers = new_arr; io_cap = new_cap; } return &io_handlers[io_len++];}
static timer_handler_t *alloc_timer(void){ for (size_t i = 0; i < tm_len; i++) { if (!tm_handlers[i].used) return &tm_handlers[i]; } if (tm_len >= tm_cap) { size_t new_cap = tm_cap ? tm_cap * 2 : 4; timer_handler_t *new_arr = realloc(tm_handlers, new_cap * sizeof *new_arr); if (!new_arr) return nullptr; tm_handlers = new_arr; tm_cap = new_cap; } return &tm_handlers[tm_len++];}
#ifdef __linux__static uint64_t get_time_ms(void){ struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;}
bool event_loop_init(void){ return (poll_fd = epoll_create1(EPOLL_CLOEXEC)) >= 0;}
bool event_loop_add_io(int fd, uint32_t events, io_callback_t fn, void *data){ io_handler_t *h = alloc_io(); if (!h) return false;
*h = (io_handler_t){.fd = fd, .events = events, .fn = fn, .data = data};
struct epoll_event ev = {.events = events, .data.ptr = h}; if (epoll_ctl(poll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { h->fd = -1; return false; } return true;}
bool event_loop_remove_io(int fd){ if (fd < 0) return false; epoll_ctl(poll_fd, EPOLL_CTL_DEL, fd, nullptr); for (size_t i = 0; i < io_len; i++) { if (io_handlers[i].fd == fd) { io_handlers[i].fd = -1; return true; } } return false;}
bool event_loop_add_timer(uint64_t interval_ms, bool repeat, timer_callback_t fn, void *data){ timer_handler_t *th = alloc_timer(); if (!th) return false;
*th = (timer_handler_t){ .interval_ms = interval_ms, .expire_at = get_time_ms() + interval_ms, .repeat = repeat, .fn = fn, .data = data, .used = true, }; return true;}
void event_loop_set_idle(idle_callback_t fn, void *data){ idle_fn = fn; idle_data = data;}
void event_loop_run(void){ running = true; struct epoll_event evs[32];
while (running) { uint64_t now = get_time_ms(); int timeout_ms = -1; uint64_t next_expire = -1ULL; bool has_timer = false;
for (size_t i = 0; i < tm_len; i++) { if (tm_handlers[i].used && tm_handlers[i].expire_at < next_expire) { next_expire = tm_handlers[i].expire_at; has_timer = true; } }
if (has_timer) { timeout_ms = (next_expire <= now) ? 0 : (int)(next_expire - now); }
int n = epoll_wait(poll_fd, evs, 32, timeout_ms); if (n < 0) { if (errno == EINTR) continue; break; }
for (int i = 0; i < n; i++) { io_handler_t *h = evs[i].data.ptr; if (h && h->fd >= 0) h->fn(h->fd, evs[i].events, h->data); }
now = get_time_ms(); for (size_t i = 0; i < tm_len; i++) { if (tm_handlers[i].used && now >= tm_handlers[i].expire_at) { tm_handlers[i].fn(tm_handlers[i].data); if (tm_handlers[i].repeat) { tm_handlers[i].expire_at = now + tm_handlers[i].interval_ms; } else { tm_handlers[i].used = false; } } }
if (idle_fn) idle_fn(idle_data); }}
void event_loop_stop(void){ running = false;}
void event_loop_destroy(void){ if (poll_fd >= 0) close(poll_fd); poll_fd = -1; free(io_handlers); free(tm_handlers); io_handlers = nullptr; tm_handlers = nullptr; io_len = io_cap = 0; tm_len = tm_cap = 0;}
#else
#ifdef NOTE_MSECONDSconstexpr uint32_t TIMER_FFLAGS = NOTE_MSECONDS;#define TIMER_DIVISOR 1#elseconstexpr uint32_t TIMER_FFLAGS = 0;#define TIMER_DIVISOR 1000#endif
bool event_loop_init(void){ return (poll_fd = kqueue()) >= 0;}
bool event_loop_add_io(int fd, uint32_t events, io_callback_t fn, void *data){ io_handler_t *h = alloc_io(); if (!h) return false;
*h = (io_handler_t){.fd = fd, .events = events, .fn = fn, .data = data};
struct kevent kev[2]; int n = 0; if (events & EVENT_READ) EV_SET(&kev[n++], fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, h); if (events & EVENT_WRITE) EV_SET(&kev[n++], fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, h);
if (n && kevent(poll_fd, kev, n, nullptr, 0, nullptr) < 0) { h->fd = -1; return false; } return true;}
bool event_loop_remove_io(int fd){ if (fd < 0) return false; struct kevent kev[2]; EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr); EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr); kevent(poll_fd, kev, 2, nullptr, 0, nullptr);
for (size_t i = 0; i < io_len; i++) { if (io_handlers[i].fd == fd) { io_handlers[i].fd = -1; return true; } } return false;}
bool event_loop_add_timer(uint64_t interval_ms, bool repeat, timer_callback_t fn, void *data){ timer_handler_t *th = alloc_timer(); if (!th) return false;
*th = (timer_handler_t){ .interval_ms = interval_ms, .repeat = repeat, .fn = fn, .data = data, .used = true, };
int flags = EV_ADD | EV_ENABLE; if (!repeat) flags |= EV_ONESHOT;
struct kevent kev; EV_SET(&kev, (uintptr_t)th, EVFILT_TIMER, flags, TIMER_FFLAGS, (intptr_t)(interval_ms / TIMER_DIVISOR), th);
if (kevent(poll_fd, &kev, 1, nullptr, 0, nullptr) < 0) { th->used = false; return false; } return true;}
void event_loop_set_idle(idle_callback_t fn, void *data){ idle_fn = fn; idle_data = data;}
void event_loop_run(void){ running = true; struct kevent evs[32];
while (running) { int n = kevent(poll_fd, nullptr, 0, evs, 32, nullptr); if (n < 0) { if (errno == EINTR) continue; break; } for (int i = 0; i < n; i++) { void *udata = evs[i].udata; if (!udata) continue;
if (evs[i].filter == EVFILT_READ || evs[i].filter == EVFILT_WRITE) { io_handler_t *h = udata; if (h->fd >= 0) h->fn(h->fd, (uint32_t)evs[i].filter, h->data); } else if (evs[i].filter == EVFILT_TIMER) { timer_handler_t *th = udata; if (th->used) { th->fn(th->data); if (!th->repeat) th->used = false; } } }
if (idle_fn) idle_fn(idle_data); }}
void event_loop_stop(void){ running = false;}
void event_loop_destroy(void){ if (poll_fd >= 0) close(poll_fd); poll_fd = -1; free(io_handlers); free(tm_handlers); io_handlers = nullptr; tm_handlers = nullptr; io_len = io_cap = 0; tm_len = tm_cap = 0;}
#endif