#include "fdm.h" #include #include #include #include #include #include #include #define LOG_MODULE "fdm" #define LOG_ENABLE_DBG 0 #include "log.h" struct handler { int fd; int events; fdm_handler_t callback; void *callback_data; bool deleted; }; struct fdm { int epoll_fd; bool is_polling; tll(struct handler *) fds; tll(struct handler *) deferred_delete; }; struct fdm * fdm_init(void) { int epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (epoll_fd == -1) { LOG_ERRNO("failed to create epoll FD"); return NULL; } struct fdm *fdm = malloc(sizeof(*fdm)); *fdm = (struct fdm){ .epoll_fd = epoll_fd, .is_polling = false, .fds = tll_init(), .deferred_delete = tll_init(), }; return fdm; } void fdm_destroy(struct fdm *fdm) { if (fdm == NULL) return; if (tll_length(fdm->fds) > 0) LOG_WARN("FD list not empty"); assert(tll_length(fdm->fds) == 0); assert(tll_length(fdm->deferred_delete) == 0); tll_free(fdm->fds); tll_free(fdm->deferred_delete); close(fdm->epoll_fd); free(fdm); } bool fdm_add(struct fdm *fdm, int fd, int events, fdm_handler_t handler, void *data) { #if defined(_DEBUG) tll_foreach(fdm->fds, it) { if (it->item->fd == fd) { LOG_ERR("FD=%d already registered", fd); return false; } } #endif struct handler *fd_data = malloc(sizeof(*fd_data)); *fd_data = (struct handler) { .fd = fd, .events = events, .callback = handler, .callback_data = data, .deleted = false, }; tll_push_back(fdm->fds, fd_data); struct epoll_event ev = { .events = events, .data = {.ptr = fd_data}, }; if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { LOG_ERRNO("failed to register FD=%d with epoll", fd); free(fd_data); tll_pop_back(fdm->fds); return false; } return true; } static bool fdm_del_internal(struct fdm *fdm, int fd, bool close_fd) { if (fd == -1) return true; tll_foreach(fdm->fds, it) { if (it->item->fd != fd) continue; if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0) LOG_ERRNO("failed to unregister FD=%d from epoll", fd); if (close_fd) close(it->item->fd); it->item->deleted = true; if (fdm->is_polling) tll_push_back(fdm->deferred_delete, it->item); else free(it->item); tll_remove(fdm->fds, it); return true; } LOG_ERR("no such FD: %d", fd); close(fd); return false; } bool fdm_del(struct fdm *fdm, int fd) { return fdm_del_internal(fdm, fd, true); } bool fdm_del_no_close(struct fdm *fdm, int fd) { return fdm_del_internal(fdm, fd, false); } static bool event_modify(struct fdm *fdm, struct handler *fd, int new_events) { if (new_events == fd->events) return true; struct epoll_event ev = { .events = new_events, .data = {.ptr = fd}, }; if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_MOD, fd->fd, &ev) < 0) { LOG_ERRNO("failed to modify FD=%d with epoll (events 0x%08x -> 0x%08x)", fd->fd, fd->events, new_events); return false; } fd->events = new_events; return true; } bool fdm_event_add(struct fdm *fdm, int fd, int events) { tll_foreach(fdm->fds, it) { if (it->item->fd != fd) continue; return event_modify(fdm, it->item, it->item->events | events); } LOG_ERR("FD=%d not registered with the FDM", fd); return false; } bool fdm_event_del(struct fdm *fdm, int fd, int events) { tll_foreach(fdm->fds, it) { if (it->item->fd != fd) continue; return event_modify(fdm, it->item, it->item->events & ~events); } LOG_ERR("FD=%d not registered with the FDM", fd); return false; } bool fdm_poll(struct fdm *fdm) { assert(!fdm->is_polling && "nested calls to fdm_poll() not allowed"); if (fdm->is_polling) { LOG_ERR("nested calls to fdm_poll() not allowed"); return false; } struct epoll_event events[tll_length(fdm->fds)]; int r = epoll_wait(fdm->epoll_fd, events, tll_length(fdm->fds), -1); if (r == -1) { if (errno == EINTR) return true; LOG_ERRNO("failed to epoll"); return false; } bool ret = true; fdm->is_polling = true; for (int i = 0; i < r; i++) { struct handler *fd = events[i].data.ptr; if (fd->deleted) continue; if (!fd->callback(fdm, fd->fd, events[i].events, fd->callback_data)) { ret = false; break; } } fdm->is_polling = false; tll_foreach(fdm->deferred_delete, it) { free(it->item); tll_remove(fdm->deferred_delete, it); } return ret; }