diff options
Diffstat (limited to 'nio.c')
-rw-r--r-- | nio.c | 417 |
1 files changed, 417 insertions, 0 deletions
@@ -0,0 +1,417 @@ +#include "nio.h" + +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#ifdef ENABLE_EPOLL +#include <sys/epoll.h> +#endif +#include <unistd.h> + +void nio_init(struct nio * io) +{ + io->nready = -1; + io->poll_max_fds = 0; + io->poll_fds = NULL; + io->poll_ptrs = NULL; + io->poll_fds_set = NULL; + io->epoll_fd = -1; + io->max_events = 0; + io->events = NULL; +} + +int nio_use_poll(struct nio * io, nfds_t max_fds) +{ + if (io->epoll_fd != -1 || io->poll_max_fds != 0 || max_fds <= 0) + return NIO_ERROR_INTERNAL; + + io->poll_max_fds = max_fds; + io->poll_fds = (struct pollfd *)calloc(max_fds, sizeof(*io->poll_fds)); + io->poll_ptrs = calloc(max_fds, sizeof(*io->poll_ptrs)); + io->poll_fds_set = calloc(max_fds, sizeof(*io->poll_fds_set)); + + for (size_t i = 0; i < max_fds; ++i) + { + io->poll_fds[i].fd = -1; + } + + return io->poll_fds == NULL || io->poll_ptrs == NULL || io->poll_fds_set == NULL; // return NIO_ERROR_INTERNAL on + // error +} + +int nio_use_epoll(struct nio * io, int max_events) +{ +#ifdef ENABLE_EPOLL + if (io->epoll_fd != -1 || io->poll_max_fds != 0 || max_events == 0) + return NIO_ERROR_INTERNAL; + + io->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + io->max_events = max_events; + io->events = calloc(max_events, sizeof(struct epoll_event)); + + return io->events == NULL || io->epoll_fd < 0; // return NIO_ERROR_INTERNAL on error +#else + (void)io; + (void)max_events; + + return NIO_ERROR_INTERNAL; +#endif +} + +int nio_add_fd(struct nio * io, int fd, int event_flags, void * ptr) +{ + if (fd < 0) + return NIO_ERROR_INTERNAL; + +#ifdef ENABLE_EPOLL + if (io->epoll_fd >= 0) + { + int rv; + struct epoll_event event = {}; + + if (ptr == NULL) + { + event.data.fd = fd; + } + else + { + event.data.ptr = ptr; + } + + if ((event_flags & NIO_EVENT_INPUT) != 0) + event.events |= EPOLLIN; + if ((event_flags & NIO_EVENT_OUTPUT) != 0) + event.events |= EPOLLOUT; + if (event.events == 0) + return NIO_ERROR_INTERNAL; + + while ((rv = epoll_ctl(io->epoll_fd, EPOLL_CTL_ADD, fd, &event)) != 0 && errno == EINTR) + { + /* If epoll_ctl() was interrupted by the system, repeat. */ + } + return rv; + } + else +#endif + if (io->poll_max_fds > 0) + { + struct pollfd * unused_pollfd = NULL; + void ** unused_ptr = NULL; + + for (size_t i = 0; i < io->poll_max_fds; ++i) + { + if (io->poll_fds[i].fd < 0) + { + unused_pollfd = &io->poll_fds[i]; + unused_ptr = &io->poll_ptrs[i]; + break; + } + } + if (unused_pollfd == NULL) + return NIO_ERROR_INTERNAL; + + unused_pollfd->events = 0; + if ((event_flags & NIO_EVENT_INPUT) != 0) + unused_pollfd->events |= POLLIN; + if ((event_flags & NIO_EVENT_OUTPUT) != 0) + unused_pollfd->events |= POLLOUT; + if (unused_pollfd->events == 0) + return NIO_ERROR_INTERNAL; + + unused_pollfd->fd = fd; + *unused_ptr = ptr; + + return NIO_SUCCESS; + } + + return NIO_ERROR_INTERNAL; +} + +int nio_mod_fd(struct nio * io, int fd, int event_flags, void * ptr) +{ + if (fd < 0) + return NIO_ERROR_INTERNAL; + +#ifdef ENABLE_EPOLL + if (io->epoll_fd >= 0) + { + int rv; + struct epoll_event event = {}; + + if (ptr == NULL) + { + event.data.fd = fd; + } + else + { + event.data.ptr = ptr; + } + + if ((event_flags & NIO_EVENT_INPUT) != 0) + event.events |= EPOLLIN; + if ((event_flags & NIO_EVENT_OUTPUT) != 0) + event.events |= EPOLLOUT; + if (event.events == 0) + return NIO_ERROR_INTERNAL; + + while ((rv = epoll_ctl(io->epoll_fd, EPOLL_CTL_MOD, fd, &event)) != 0 && errno == EINTR) + { + /* If epoll_ctl() was interrupted by the system, repeat. */ + } + return rv; + } + else +#endif + if (io->poll_max_fds > 0) + { + struct pollfd * used_pollfd = NULL; + void ** used_ptr = NULL; + + for (size_t i = 0; i < io->poll_max_fds; ++i) + { + if (io->poll_fds[i].fd == fd) + { + used_pollfd = &io->poll_fds[i]; + used_ptr = &io->poll_ptrs[i]; + break; + } + } + if (used_pollfd == NULL) + return NIO_ERROR_INTERNAL; + + used_pollfd->events = 0; + if ((event_flags & NIO_EVENT_INPUT) != 0) + used_pollfd->events |= POLLIN; + if ((event_flags & NIO_EVENT_OUTPUT) != 0) + used_pollfd->events |= POLLOUT; + if (used_pollfd->events == 0) + return NIO_ERROR_INTERNAL; + + used_pollfd->fd = fd; + *used_ptr = ptr; + + return NIO_SUCCESS; + } + + return NIO_ERROR_INTERNAL; +} + +int nio_del_fd(struct nio * io, int fd) +{ + if (fd < 0) + return NIO_ERROR_INTERNAL; + +#ifdef ENABLE_EPOLL + if (io->epoll_fd >= 0) + { + int rv; + + while ((rv = epoll_ctl(io->epoll_fd, EPOLL_CTL_DEL, fd, NULL)) != 0 && errno == EINTR) + { + /* If epoll_ctl() was interrupted by the system, repeat. */ + } + return rv; + } + else +#endif + if (io->poll_max_fds > 0) + { + struct pollfd * used_pollfd = NULL; + void ** used_ptr = NULL; + + for (size_t i = 0; i < io->poll_max_fds; ++i) + { + if (io->poll_fds[i].fd == fd) + { + used_pollfd = &io->poll_fds[i]; + used_ptr = &io->poll_ptrs[i]; + break; + } + } + if (used_pollfd == NULL) + return NIO_ERROR_INTERNAL; + + used_pollfd->fd = -1; + *used_ptr = NULL; + + return NIO_SUCCESS; + } + + return NIO_ERROR_INTERNAL; +} + +int nio_run(struct nio * io, int timeout) +{ +#ifdef ENABLE_EPOLL + if (io->epoll_fd >= 0) + { + do + { + io->nready = epoll_wait(io->epoll_fd, io->events, io->max_events, timeout); + } while (io->nready < 0 && errno == EINTR); + + if (io->nready < 0) + return NIO_ERROR_SYSTEM; + } + else +#endif + if (io->poll_max_fds > 0) + { + do + { + io->nready = poll(io->poll_fds, io->poll_max_fds, timeout); + } while (io->nready < 0 && errno == EINTR); + + if (io->nready < 0) + return NIO_ERROR_SYSTEM; + + if (io->nready > 0) + { + for (nfds_t i = 0, j = 0; i < io->poll_max_fds; ++i) + { + if (io->poll_fds[i].fd >= 0 && io->poll_fds[i].revents != 0) + { + io->poll_fds_set[j++] = i; + } + } + } + } + + return NIO_SUCCESS; +} + +int nio_check(struct nio * io, int index, int event_flags) +{ + if (nio_is_valid(io, index) != NIO_SUCCESS) + return NIO_ERROR_INTERNAL; + +#ifdef ENABLE_EPOLL + if (io->epoll_fd >= 0) + { + uint32_t epoll_events = 0; + + if ((event_flags & NIO_EVENT_INPUT) != 0) + epoll_events |= EPOLLIN; + if ((event_flags & NIO_EVENT_OUTPUT) != 0) + epoll_events |= EPOLLOUT; + if ((event_flags & NIO_EVENT_ERROR) != 0) + epoll_events |= EPOLLERR | EPOLLHUP; + if (epoll_events == 0) + return NIO_ERROR_INTERNAL; + + struct epoll_event const * const events = (struct epoll_event *)io->events; + if ((events[index].events & epoll_events) == 0) + return NIO_ERROR_INTERNAL; + + return NIO_SUCCESS; + } + else +#endif + if (io->poll_max_fds > 0) + { + short int poll_events = 0; + + if ((event_flags & NIO_EVENT_INPUT) != 0) + poll_events |= POLLIN; + if ((event_flags & NIO_EVENT_OUTPUT) != 0) + poll_events |= POLLOUT; + if ((event_flags & NIO_EVENT_ERROR) != 0) + poll_events |= POLLERR | POLLHUP; + if (poll_events == 0) + return NIO_ERROR_INTERNAL; + + if ((io->poll_fds[io->poll_fds_set[index]].revents & poll_events) == 0) + return NIO_ERROR_INTERNAL; + + return NIO_SUCCESS; + } + + return NIO_ERROR_INTERNAL; +} + +int nio_is_valid(struct nio const * const io, int index) +{ + if (index < 0 || index >= io->nready) + return NIO_ERROR_INTERNAL; + +#ifdef ENABLE_EPOLL + if (io->epoll_fd >= 0) + { + return NIO_SUCCESS; + } + else +#endif + if (io->poll_max_fds > 0 && io->poll_fds[io->poll_fds_set[index]].fd >= 0) + { + return NIO_SUCCESS; + } + + return NIO_ERROR_INTERNAL; +} + +int nio_get_fd(struct nio * io, int index) +{ + if (nio_is_valid(io, index) != NIO_SUCCESS) + return -1; + +#ifdef ENABLE_EPOLL + if (io->epoll_fd >= 0) + { + struct epoll_event const * const events = (struct epoll_event *)io->events; + + return events[index].data.fd; + } + else +#endif + if (io->poll_max_fds > 0) + { + return io->poll_fds[io->poll_fds_set[index]].fd; + } + + return -1; +} + +void * nio_get_ptr(struct nio * io, int index) +{ + if (nio_is_valid(io, index) != NIO_SUCCESS) + return NULL; + +#ifdef ENABLE_EPOLL + if (io->epoll_fd >= 0) + { + struct epoll_event * const events = (struct epoll_event *)io->events; + + return events[index].data.ptr; + } + else +#endif + if (io->poll_max_fds > 0) + { + return io->poll_ptrs[io->poll_fds_set[index]]; + } + + return NULL; +} + +void nio_free(struct nio * io) +{ + for (size_t i = 0; i < io->poll_max_fds; ++i) + { + if (io->poll_fds[i].fd >= 0) + { + close(io->poll_fds[i].fd); + io->poll_fds[i].fd = -1; + } + } +#ifdef ENABLE_EPOLL + if (io->epoll_fd >= 0) + { + close(io->epoll_fd); + io->epoll_fd = -1; + } +#endif + free(io->poll_fds); + free(io->poll_ptrs); + free(io->poll_fds_set); + free(io->events); +} |