/* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the Lesser GNU General Public License, version 3 * or later ("LGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the Lesser GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include "fc_memory.h" #include "ioevent.h" #if IOEVENT_USE_KQUEUE /* we define these here as numbers, because for kqueue mapping them to a combination of * filters / flags is hard to do. */ int kqueue_ev_convert(int16_t event, uint16_t flags) { int r; if (event == EVFILT_READ) { r = KPOLLIN; } else if (event == EVFILT_WRITE) { r = KPOLLOUT; } else { r = 0; } if (flags & EV_EOF) { r |= KPOLLHUP; } return r; } #endif int ioevent_init(IOEventPoller *ioevent, const int size, const int timeout_ms, const int extra_events) { int bytes; ioevent->size = size; ioevent->extra_events = extra_events; ioevent->iterator.index = 0; ioevent->iterator.count = 0; #if IOEVENT_USE_EPOLL ioevent->poll_fd = epoll_create(ioevent->size); if (ioevent->poll_fd < 0) { return errno != 0 ? errno : ENOMEM; } bytes = sizeof(struct epoll_event) * size; ioevent->events = (struct epoll_event *)fc_malloc(bytes); #elif IOEVENT_USE_KQUEUE ioevent->poll_fd = kqueue(); if (ioevent->poll_fd < 0) { return errno != 0 ? errno : ENOMEM; } bytes = sizeof(struct kevent) * size; ioevent->events = (struct kevent *)fc_malloc(bytes); #elif IOEVENT_USE_PORT ioevent->poll_fd = port_create(); if (ioevent->poll_fd < 0) { return errno != 0 ? errno : ENOMEM; } bytes = sizeof(port_event_t) * size; ioevent->events = (port_event_t *)fc_malloc(bytes); #endif if (ioevent->events == NULL) { close(ioevent->poll_fd); ioevent->poll_fd = -1; return ENOMEM; } ioevent_set_timeout(ioevent, timeout_ms); return 0; } void ioevent_destroy(IOEventPoller *ioevent) { if (ioevent->events != NULL) { free(ioevent->events); ioevent->events = NULL; } if (ioevent->poll_fd >= 0) { close(ioevent->poll_fd); ioevent->poll_fd = -1; } } int ioevent_attach(IOEventPoller *ioevent, const int fd, const int e, void *data) { #if IOEVENT_USE_EPOLL struct epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = e | ioevent->extra_events; ev.data.ptr = data; return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_ADD, fd, &ev); #elif IOEVENT_USE_KQUEUE struct kevent ev[2]; int n = 0; if (e & IOEVENT_READ) { EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD | ioevent->extra_events, 0, 0, data); } if (e & IOEVENT_WRITE) { EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD | ioevent->extra_events, 0, 0, data); } if (n == 0) { return ENOENT; } return kevent(ioevent->poll_fd, ev, n, NULL, 0, NULL); #elif IOEVENT_USE_PORT return port_associate(ioevent->poll_fd, PORT_SOURCE_FD, fd, e, data); #endif } int ioevent_modify(IOEventPoller *ioevent, const int fd, const int e, void *data) { #if IOEVENT_USE_EPOLL struct epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = e | ioevent->extra_events; ev.data.ptr = data; return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_MOD, fd, &ev); #elif IOEVENT_USE_KQUEUE struct kevent ev[2]; int result; int n = 0; if (e & IOEVENT_READ) { EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD | ioevent->extra_events, 0, 0, data); } else { EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); } if (e & IOEVENT_WRITE) { EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD | ioevent->extra_events, 0, 0, data); } else { EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); } result = kevent(ioevent->poll_fd, ev, n, NULL, 0, NULL); if (result == -1) { result = ioevent_detach(ioevent, fd); if (e & (IOEVENT_READ | IOEVENT_WRITE)) { result = ioevent_attach(ioevent, fd, e, data); } } return result; #elif IOEVENT_USE_PORT return port_associate(ioevent->poll_fd, PORT_SOURCE_FD, fd, e, data); #endif } int ioevent_detach(IOEventPoller *ioevent, const int fd) { #if IOEVENT_USE_EPOLL return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_DEL, fd, NULL); #elif IOEVENT_USE_KQUEUE struct kevent ev[1]; int r, w; EV_SET(&ev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); r = kevent(ioevent->poll_fd, ev, 1, NULL, 0, NULL); EV_SET(&ev[0], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); w = kevent(ioevent->poll_fd, ev, 1, NULL, 0, NULL); return (r == 0 || w == 0) ? 0 : r; #elif IOEVENT_USE_PORT return port_dissociate(ioevent->poll_fd, PORT_SOURCE_FD, fd); #endif } int ioevent_poll(IOEventPoller *ioevent) { #if IOEVENT_USE_EPOLL return epoll_wait(ioevent->poll_fd, ioevent->events, ioevent->size, ioevent->timeout); #elif IOEVENT_USE_KQUEUE return kevent(ioevent->poll_fd, NULL, 0, ioevent->events, ioevent->size, &ioevent->timeout); #elif IOEVENT_USE_PORT int result; int retval; unsigned int nget = 1; if((retval = port_getn(ioevent->poll_fd, ioevent->events, ioevent->size, &nget, &ioevent->timeout)) == 0) { result = (int)nget; } else { switch(errno) { case EINTR: case EAGAIN: case ETIME: if (nget > 0) { result = (int)nget; } else { result = 0; } break; default: result = -1; break; } } return result; #else #error port me #endif }