You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

196 lines
4.1 KiB
C

// 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 <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#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);
}