// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. #include #include #include #include #include #include "eventloop_platform.h" #include "public/io.h" struct tlsl_event_platform { int epoll_registered_fd; bool epoll_fd_owned; }; struct tlsl_eventloop_platform { int epoll_fd; }; int tlsl_platform_el_init(struct tlsl_eventloop *el) { TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp); if ((elp->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) return -1; return 0; } void tlsl_platform_el_cleanup(struct tlsl_eventloop *el) { TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp); close(elp->epoll_fd); } void tlsl_platform_el_event_remove(struct tlsl_eventloop *el, struct tlsl_event *e) { TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp); TLSL_DEFINE_PLATFORM_EVENT_DATA(e, ep); epoll_ctl(elp->epoll_fd, EPOLL_CTL_DEL, ep->epoll_registered_fd, NULL); } void tlsl_platform_el_event_cleanup(struct tlsl_eventloop *el, struct tlsl_event *e) { TLSL_DEFINE_PLATFORM_EVENT_DATA(e, ep); (void)el; if (ep->epoll_fd_owned && (ep->epoll_registered_fd >= 0)) { close(ep->epoll_registered_fd); ep->epoll_registered_fd = -1; } } static int clear_timerfd(int fd) { uint64_t tfd_buf; ssize_t res; do { res = read(fd, &tfd_buf, sizeof(tfd_buf)); } while ((res < 0) && (errno == EINTR)); if (res < 0) return -1; return 0; } int tlsl_platform_el_event_clear(struct tlsl_eventloop *el, struct tlsl_event *e) { TLSL_DEFINE_PLATFORM_EVENT_DATA(e, ep); int res; (void)el; if ((e->id.source == TLSL_EVENT_SOURCE_TIMER) && (ep->epoll_registered_fd >= 0)) { if ((res = clear_timerfd(ep->epoll_registered_fd))) return res; } return 0; } int tlsl_platform_el_wait( struct tlsl_eventloop *el, tlsl_event_id_t *id_out, uint32_t *flags_out, int timeout_ms) { TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp); struct epoll_event ev; struct tlsl_event *e; int res; if (timeout_ms < 0) timeout_ms = -1; do { res = epoll_pwait(elp->epoll_fd, &ev, 1, timeout_ms, NULL); } while ((res < 0) && (errno == EINTR)); if (res <= 0) { if (!res) errno = ETIMEDOUT; return -1; } e = (struct tlsl_event *)ev.data.ptr; *id_out = e->id; *flags_out = 0; if (ev.events & EPOLLIN) *flags_out |= TLSL_EVENT_READABLE; if (ev.events & EPOLLOUT) *flags_out |= TLSL_EVENT_WRITABLE; return 0; } static int el_epoll_add( struct tlsl_eventloop *el, struct tlsl_event *e, int fd, enum tlsl_event_source source, uint32_t events, bool owned) { TLSL_DEFINE_PLATFORM_EVENTLOOP_DATA(el, elp); TLSL_DEFINE_PLATFORM_EVENT_DATA(e, ep); struct epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = events; ev.data.fd = fd; ev.data.ptr = e; if (epoll_ctl(elp->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) return -1; e->id.obj = (uintptr_t)fd; e->id.source = source; ep->epoll_registered_fd = fd; ep->epoll_fd_owned = owned; return 0; } int tlsl_platform_el_add_timer( struct tlsl_eventloop *el, struct tlsl_event *e, uint64_t millis, bool oneshot) { FD_SCOPED(tfd); struct itimerspec spec; struct timespec ts; int res; ts.tv_sec = (long)(millis / 1000); ts.tv_nsec = (long)((millis % 1000) * 1000000); memset(&spec, 0, sizeof(spec)); spec.it_value = ts; if (!oneshot) spec.it_interval = ts; if ((tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)) < 0) return -1; if (timerfd_settime(tfd, 0, &spec, NULL)) return -1; if ((res = el_epoll_add(el, e, tfd, TLSL_EVENT_SOURCE_TIMER, EPOLLIN, true))) return res; FD_RELEASE(&tfd); return 0; } int tlsl_platform_el_add_fd( struct tlsl_eventloop *el, struct tlsl_event *e, int fd, uint32_t flags) { uint32_t events; events = 0; if (flags & TLSL_EVENT_READABLE) events |= EPOLLIN; if (flags & TLSL_EVENT_WRITABLE) events |= EPOLLOUT; return el_epoll_add(el, e, fd, TLSL_EVENT_SOURCE_FD, events, false); }