#include "bar/event.h" #include #include #include #include #include #if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && \ !defined(__NetBSD__) && !defined(__DragonFly__) #error "no epoll or kqueue support detected" #endif #ifdef __linux__ #include #include #else #include #include #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_MSECONDS constexpr uint32_t TIMER_FFLAGS = NOTE_MSECONDS; #define TIMER_DIVISOR 1 #else constexpr 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