mirror of
https://github.com/bol-van/zapret.git
synced 2025-05-24 22:32:58 +03:00
Truncated history
This commit is contained in:
80
tpws/epoll-shim/include/sys/epoll.h
Normal file
80
tpws/epoll-shim/include/sys/epoll.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef SHIM_SYS_EPOLL_H
|
||||
#define SHIM_SYS_EPOLL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#include <sys/sigtypes.h>
|
||||
#elif defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__APPLE__)
|
||||
#include <sys/signal.h>
|
||||
#endif
|
||||
|
||||
#define EPOLL_CLOEXEC O_CLOEXEC
|
||||
#define EPOLL_NONBLOCK O_NONBLOCK
|
||||
|
||||
enum EPOLL_EVENTS { __EPOLL_DUMMY };
|
||||
#define EPOLLIN 0x001
|
||||
#define EPOLLPRI 0x002
|
||||
#define EPOLLOUT 0x004
|
||||
#define EPOLLRDNORM 0x040
|
||||
#define EPOLLNVAL 0x020
|
||||
#define EPOLLRDBAND 0x080
|
||||
#define EPOLLWRNORM 0x100
|
||||
#define EPOLLWRBAND 0x200
|
||||
#define EPOLLMSG 0x400
|
||||
#define EPOLLERR 0x008
|
||||
#define EPOLLHUP 0x010
|
||||
#define EPOLLRDHUP 0x2000
|
||||
#define EPOLLEXCLUSIVE (1U<<28)
|
||||
#define EPOLLWAKEUP (1U<<29)
|
||||
#define EPOLLONESHOT (1U<<30)
|
||||
#define EPOLLET (1U<<31)
|
||||
|
||||
#define EPOLL_CTL_ADD 1
|
||||
#define EPOLL_CTL_DEL 2
|
||||
#define EPOLL_CTL_MOD 3
|
||||
|
||||
typedef union epoll_data {
|
||||
void *ptr;
|
||||
int fd;
|
||||
uint32_t u32;
|
||||
uint64_t u64;
|
||||
} epoll_data_t;
|
||||
|
||||
struct epoll_event {
|
||||
uint32_t events;
|
||||
epoll_data_t data;
|
||||
}
|
||||
#ifdef __x86_64__
|
||||
__attribute__ ((__packed__))
|
||||
#endif
|
||||
;
|
||||
|
||||
|
||||
int epoll_create(int);
|
||||
int epoll_create1(int);
|
||||
int epoll_ctl(int, int, int, struct epoll_event *);
|
||||
int epoll_wait(int, struct epoll_event *, int, int);
|
||||
int epoll_pwait(int, struct epoll_event *, int, int, const sigset_t *);
|
||||
|
||||
|
||||
#ifndef SHIM_SYS_SHIM_HELPERS
|
||||
#define SHIM_SYS_SHIM_HELPERS
|
||||
#include <unistd.h> /* IWYU pragma: keep */
|
||||
|
||||
extern int epoll_shim_close(int);
|
||||
#define close epoll_shim_close
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* sys/epoll.h */
|
305
tpws/epoll-shim/src/epoll.c
Normal file
305
tpws/epoll-shim/src/epoll.c
Normal file
@@ -0,0 +1,305 @@
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include <sys/event.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "epoll_shim_ctx.h"
|
||||
|
||||
#ifdef __NetBSD__
|
||||
#define ppoll pollts
|
||||
#endif
|
||||
|
||||
// TODO(jan): Remove this once the definition is exposed in <sys/time.h> in
|
||||
// all supported FreeBSD versions.
|
||||
#ifndef timespecsub
|
||||
#define timespecsub(tsp, usp, vsp) \
|
||||
do { \
|
||||
(vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
|
||||
(vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
|
||||
if ((vsp)->tv_nsec < 0) { \
|
||||
(vsp)->tv_sec--; \
|
||||
(vsp)->tv_nsec += 1000000000L; \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static errno_t
|
||||
epollfd_close(FDContextMapNode *node)
|
||||
{
|
||||
return epollfd_ctx_terminate(&node->ctx.epollfd);
|
||||
}
|
||||
|
||||
static FDContextVTable const epollfd_vtable = {
|
||||
.read_fun = fd_context_default_read,
|
||||
.write_fun = fd_context_default_write,
|
||||
.close_fun = epollfd_close,
|
||||
};
|
||||
|
||||
static FDContextMapNode *
|
||||
epoll_create_impl(errno_t *ec)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
|
||||
node = epoll_shim_ctx_create_node(&epoll_shim_ctx, ec);
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node->flags = 0;
|
||||
|
||||
if ((*ec = epollfd_ctx_init(&node->ctx.epollfd, /**/
|
||||
node->fd)) != 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
node->vtable = &epollfd_vtable;
|
||||
return node;
|
||||
|
||||
fail:
|
||||
epoll_shim_ctx_remove_node_explicit(&epoll_shim_ctx, node);
|
||||
(void)fd_context_map_node_destroy(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
epoll_create_common(void)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
errno_t ec;
|
||||
|
||||
node = epoll_create_impl(&ec);
|
||||
if (!node) {
|
||||
errno = ec;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return node->fd;
|
||||
}
|
||||
|
||||
int
|
||||
epoll_create(int size)
|
||||
{
|
||||
if (size <= 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return epoll_create_common();
|
||||
}
|
||||
|
||||
int
|
||||
epoll_create1(int flags)
|
||||
{
|
||||
if (flags & ~EPOLL_CLOEXEC) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return epoll_create_common();
|
||||
}
|
||||
|
||||
static errno_t
|
||||
epoll_ctl_impl(int fd, int op, int fd2, struct epoll_event *ev)
|
||||
{
|
||||
if (!ev && op != EPOLL_CTL_DEL) {
|
||||
return EFAULT;
|
||||
}
|
||||
|
||||
FDContextMapNode *node = epoll_shim_ctx_find_node(&epoll_shim_ctx, fd);
|
||||
if (!node || node->vtable != &epollfd_vtable) {
|
||||
struct stat sb;
|
||||
return (fd < 0 || fstat(fd, &sb) < 0) ? EBADF : EINVAL;
|
||||
}
|
||||
|
||||
return epollfd_ctx_ctl(&node->ctx.epollfd, op, fd2, ev);
|
||||
}
|
||||
|
||||
int
|
||||
epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev)
|
||||
{
|
||||
errno_t ec = epoll_ctl_impl(fd, op, fd2, ev);
|
||||
if (ec != 0) {
|
||||
errno = ec;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_no_wait_deadline(struct timespec const *deadline)
|
||||
{
|
||||
return (deadline && deadline->tv_sec == 0 && deadline->tv_nsec == 0);
|
||||
}
|
||||
|
||||
static errno_t
|
||||
epollfd_ctx_wait_or_block(EpollFDCtx *epollfd, struct epoll_event *ev, int cnt,
|
||||
int *actual_cnt, struct timespec const *deadline, sigset_t const *sigs)
|
||||
{
|
||||
errno_t ec;
|
||||
|
||||
for (;;) {
|
||||
if ((ec = epollfd_ctx_wait(epollfd, /**/
|
||||
ev, cnt, actual_cnt)) != 0) {
|
||||
return ec;
|
||||
}
|
||||
|
||||
if (*actual_cnt || is_no_wait_deadline(deadline)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct timespec timeout;
|
||||
|
||||
if (deadline) {
|
||||
struct timespec current_time;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, /**/
|
||||
¤t_time) < 0) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
timespecsub(deadline, ¤t_time, &timeout);
|
||||
if (timeout.tv_sec < 0 ||
|
||||
is_no_wait_deadline(&timeout)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
(void)pthread_mutex_lock(&epollfd->mutex);
|
||||
|
||||
nfds_t nfds = (nfds_t)(1 + epollfd->poll_fds_size);
|
||||
|
||||
size_t size;
|
||||
if (__builtin_mul_overflow(nfds, sizeof(struct pollfd),
|
||||
&size)) {
|
||||
ec = ENOMEM;
|
||||
(void)pthread_mutex_unlock(&epollfd->mutex);
|
||||
return ec;
|
||||
}
|
||||
|
||||
struct pollfd *pfds = malloc(size);
|
||||
if (!pfds) {
|
||||
ec = errno;
|
||||
(void)pthread_mutex_unlock(&epollfd->mutex);
|
||||
return ec;
|
||||
}
|
||||
|
||||
epollfd_ctx_fill_pollfds(epollfd, pfds);
|
||||
|
||||
(void)pthread_mutex_lock(&epollfd->nr_polling_threads_mutex);
|
||||
++epollfd->nr_polling_threads;
|
||||
(void)pthread_mutex_unlock(&epollfd->nr_polling_threads_mutex);
|
||||
|
||||
(void)pthread_mutex_unlock(&epollfd->mutex);
|
||||
|
||||
/*
|
||||
* This surfaced a race condition when
|
||||
* registering/unregistering poll-only fds. The tests should
|
||||
* still succeed if this is enabled.
|
||||
*/
|
||||
#if 0
|
||||
usleep(500000);
|
||||
#endif
|
||||
|
||||
int n = ppoll(pfds, nfds, deadline ? &timeout : NULL, sigs);
|
||||
if (n < 0) {
|
||||
ec = errno;
|
||||
}
|
||||
|
||||
free(pfds);
|
||||
|
||||
(void)pthread_mutex_lock(&epollfd->nr_polling_threads_mutex);
|
||||
--epollfd->nr_polling_threads;
|
||||
if (epollfd->nr_polling_threads == 0) {
|
||||
(void)pthread_cond_signal(
|
||||
&epollfd->nr_polling_threads_cond);
|
||||
}
|
||||
(void)pthread_mutex_unlock(&epollfd->nr_polling_threads_mutex);
|
||||
|
||||
if (n < 0) {
|
||||
return ec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static errno_t
|
||||
timeout_to_deadline(struct timespec *deadline, int to)
|
||||
{
|
||||
assert(to >= 0);
|
||||
|
||||
if (to == 0) {
|
||||
*deadline = (struct timespec){0, 0};
|
||||
} else if (to > 0) {
|
||||
if (clock_gettime(CLOCK_MONOTONIC, deadline) < 0) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (__builtin_add_overflow(deadline->tv_sec, to / 1000 + 1,
|
||||
&deadline->tv_sec)) {
|
||||
return EINVAL;
|
||||
}
|
||||
deadline->tv_sec -= 1;
|
||||
|
||||
deadline->tv_nsec += (to % 1000) * 1000000L;
|
||||
if (deadline->tv_nsec >= 1000000000) {
|
||||
deadline->tv_nsec -= 1000000000;
|
||||
deadline->tv_sec += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static errno_t
|
||||
epoll_pwait_impl(int fd, struct epoll_event *ev, int cnt, int to,
|
||||
sigset_t const *sigs, int *actual_cnt)
|
||||
{
|
||||
if (cnt < 1 || cnt > (int)(INT_MAX / sizeof(struct epoll_event))) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
FDContextMapNode *node = epoll_shim_ctx_find_node(&epoll_shim_ctx, fd);
|
||||
if (!node || node->vtable != &epollfd_vtable) {
|
||||
struct stat sb;
|
||||
return (fd < 0 || fstat(fd, &sb) < 0) ? EBADF : EINVAL;
|
||||
}
|
||||
|
||||
struct timespec deadline;
|
||||
errno_t ec;
|
||||
if (to >= 0 && (ec = timeout_to_deadline(&deadline, to)) != 0) {
|
||||
return ec;
|
||||
}
|
||||
|
||||
return epollfd_ctx_wait_or_block(&node->ctx.epollfd, ev, cnt,
|
||||
actual_cnt, (to >= 0) ? &deadline : NULL, sigs);
|
||||
}
|
||||
|
||||
int
|
||||
epoll_pwait(int fd, struct epoll_event *ev, int cnt, int to,
|
||||
sigset_t const *sigs)
|
||||
{
|
||||
int actual_cnt;
|
||||
|
||||
errno_t ec = epoll_pwait_impl(fd, ev, cnt, to, sigs, &actual_cnt);
|
||||
if (ec != 0) {
|
||||
errno = ec;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return actual_cnt;
|
||||
}
|
||||
|
||||
int
|
||||
epoll_wait(int fd, struct epoll_event *ev, int cnt, int to)
|
||||
{
|
||||
return epoll_pwait(fd, ev, cnt, to, NULL);
|
||||
}
|
281
tpws/epoll-shim/src/epoll_shim_ctx.c
Normal file
281
tpws/epoll-shim/src/epoll_shim_ctx.c
Normal file
@@ -0,0 +1,281 @@
|
||||
#include "epoll_shim_ctx.h"
|
||||
|
||||
#include <sys/event.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void
|
||||
fd_context_map_node_init(FDContextMapNode *node, int kq)
|
||||
{
|
||||
node->fd = kq;
|
||||
node->vtable = NULL;
|
||||
}
|
||||
|
||||
static FDContextMapNode *
|
||||
fd_context_map_node_create(int kq, errno_t *ec)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
|
||||
node = malloc(sizeof(FDContextMapNode));
|
||||
if (!node) {
|
||||
*ec = errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fd_context_map_node_init(node, kq);
|
||||
return node;
|
||||
}
|
||||
|
||||
static errno_t
|
||||
fd_context_map_node_terminate(FDContextMapNode *node, bool close_fd)
|
||||
{
|
||||
errno_t ec = node->vtable ? node->vtable->close_fun(node) : 0;
|
||||
|
||||
if (close_fd && close(node->fd) < 0) {
|
||||
ec = ec ? ec : errno;
|
||||
}
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
errno_t
|
||||
fd_context_map_node_destroy(FDContextMapNode *node)
|
||||
{
|
||||
errno_t ec = fd_context_map_node_terminate(node, true);
|
||||
free(node);
|
||||
return ec;
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
errno_t
|
||||
fd_context_default_read(FDContextMapNode *node, /**/
|
||||
void *buf, size_t nbytes, size_t *bytes_transferred)
|
||||
{
|
||||
(void)node;
|
||||
(void)buf;
|
||||
(void)nbytes;
|
||||
(void)bytes_transferred;
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
errno_t
|
||||
fd_context_default_write(FDContextMapNode *node, /**/
|
||||
void const *buf, size_t nbytes, size_t *bytes_transferred)
|
||||
{
|
||||
(void)node;
|
||||
(void)buf;
|
||||
(void)nbytes;
|
||||
(void)bytes_transferred;
|
||||
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
static int
|
||||
fd_context_map_node_cmp(FDContextMapNode *e1, FDContextMapNode *e2)
|
||||
{
|
||||
return (e1->fd < e2->fd) ? -1 : (e1->fd > e2->fd);
|
||||
}
|
||||
|
||||
RB_PROTOTYPE_STATIC(fd_context_map_, fd_context_map_node_, entry,
|
||||
fd_context_map_node_cmp);
|
||||
RB_GENERATE_STATIC(fd_context_map_, fd_context_map_node_, entry,
|
||||
fd_context_map_node_cmp);
|
||||
|
||||
EpollShimCtx epoll_shim_ctx = {
|
||||
.fd_context_map = RB_INITIALIZER(&fd_context_map),
|
||||
.mutex = PTHREAD_MUTEX_INITIALIZER,
|
||||
};
|
||||
|
||||
static FDContextMapNode *
|
||||
epoll_shim_ctx_create_node_impl(EpollShimCtx *epoll_shim_ctx, int kq,
|
||||
errno_t *ec)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
|
||||
{
|
||||
FDContextMapNode find;
|
||||
find.fd = kq;
|
||||
|
||||
node = RB_FIND(fd_context_map_, /**/
|
||||
&epoll_shim_ctx->fd_context_map, &find);
|
||||
}
|
||||
|
||||
if (node) {
|
||||
/*
|
||||
* If we get here, someone must have already closed the old fd
|
||||
* with a normal 'close()' call, i.e. not with our
|
||||
* 'epoll_shim_close()' wrapper. The fd inside the node
|
||||
* refers now to the new kq we are currently creating. We
|
||||
* must not close it, but we must clean up the old context
|
||||
* object!
|
||||
*/
|
||||
(void)fd_context_map_node_terminate(node, false);
|
||||
fd_context_map_node_init(node, kq);
|
||||
} else {
|
||||
node = fd_context_map_node_create(kq, ec);
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *colliding_node = RB_INSERT(fd_context_map_,
|
||||
&epoll_shim_ctx->fd_context_map, node);
|
||||
(void)colliding_node;
|
||||
assert(colliding_node == NULL);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
FDContextMapNode *
|
||||
epoll_shim_ctx_create_node(EpollShimCtx *epoll_shim_ctx, errno_t *ec)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
|
||||
int kq = kqueue();
|
||||
if (kq < 0) {
|
||||
*ec = errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(void)pthread_mutex_lock(&epoll_shim_ctx->mutex);
|
||||
node = epoll_shim_ctx_create_node_impl(epoll_shim_ctx, kq, ec);
|
||||
(void)pthread_mutex_unlock(&epoll_shim_ctx->mutex);
|
||||
|
||||
if (!node) {
|
||||
close(kq);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static FDContextMapNode *
|
||||
epoll_shim_ctx_find_node_impl(EpollShimCtx *epoll_shim_ctx, int fd)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
|
||||
FDContextMapNode find;
|
||||
find.fd = fd;
|
||||
|
||||
node = RB_FIND(fd_context_map_, /**/
|
||||
&epoll_shim_ctx->fd_context_map, &find);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
FDContextMapNode *
|
||||
epoll_shim_ctx_find_node(EpollShimCtx *epoll_shim_ctx, int fd)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
|
||||
(void)pthread_mutex_lock(&epoll_shim_ctx->mutex);
|
||||
node = epoll_shim_ctx_find_node_impl(epoll_shim_ctx, fd);
|
||||
(void)pthread_mutex_unlock(&epoll_shim_ctx->mutex);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
FDContextMapNode *
|
||||
epoll_shim_ctx_remove_node(EpollShimCtx *epoll_shim_ctx, int fd)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
|
||||
(void)pthread_mutex_lock(&epoll_shim_ctx->mutex);
|
||||
node = epoll_shim_ctx_find_node_impl(epoll_shim_ctx, fd);
|
||||
if (node) {
|
||||
RB_REMOVE(fd_context_map_, /**/
|
||||
&epoll_shim_ctx->fd_context_map, node);
|
||||
}
|
||||
(void)pthread_mutex_unlock(&epoll_shim_ctx->mutex);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void
|
||||
epoll_shim_ctx_remove_node_explicit(EpollShimCtx *epoll_shim_ctx,
|
||||
FDContextMapNode *node)
|
||||
{
|
||||
(void)pthread_mutex_lock(&epoll_shim_ctx->mutex);
|
||||
RB_REMOVE(fd_context_map_, /**/
|
||||
&epoll_shim_ctx->fd_context_map, node);
|
||||
(void)pthread_mutex_unlock(&epoll_shim_ctx->mutex);
|
||||
}
|
||||
|
||||
/**/
|
||||
|
||||
int
|
||||
epoll_shim_close(int fd)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
|
||||
node = epoll_shim_ctx_remove_node(&epoll_shim_ctx, fd);
|
||||
if (!node) {
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
errno_t ec = fd_context_map_node_destroy(node);
|
||||
if (ec != 0) {
|
||||
errno = ec;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
epoll_shim_read(int fd, void *buf, size_t nbytes)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
|
||||
node = epoll_shim_ctx_find_node(&epoll_shim_ctx, fd);
|
||||
if (!node) {
|
||||
return read(fd, buf, nbytes);
|
||||
}
|
||||
|
||||
if (nbytes > SSIZE_MAX) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t bytes_transferred;
|
||||
errno_t ec = node->vtable->read_fun(node, /**/
|
||||
buf, nbytes, &bytes_transferred);
|
||||
if (ec != 0) {
|
||||
errno = ec;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (ssize_t)bytes_transferred;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
epoll_shim_write(int fd, void const *buf, size_t nbytes)
|
||||
{
|
||||
FDContextMapNode *node;
|
||||
|
||||
node = epoll_shim_ctx_find_node(&epoll_shim_ctx, fd);
|
||||
if (!node) {
|
||||
return write(fd, buf, nbytes);
|
||||
}
|
||||
|
||||
if (nbytes > SSIZE_MAX) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t bytes_transferred;
|
||||
errno_t ec = node->vtable->write_fun(node, /**/
|
||||
buf, nbytes, &bytes_transferred);
|
||||
if (ec != 0) {
|
||||
errno = ec;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (ssize_t)bytes_transferred;
|
||||
}
|
76
tpws/epoll-shim/src/epoll_shim_ctx.h
Normal file
76
tpws/epoll-shim/src/epoll_shim_ctx.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef EPOLL_SHIM_CTX_H_
|
||||
#define EPOLL_SHIM_CTX_H_
|
||||
|
||||
#include "fix.h"
|
||||
|
||||
#include <sys/tree.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "epollfd_ctx.h"
|
||||
#include "eventfd_ctx.h"
|
||||
#include "signalfd_ctx.h"
|
||||
#include "timerfd_ctx.h"
|
||||
|
||||
struct fd_context_map_node_;
|
||||
typedef struct fd_context_map_node_ FDContextMapNode;
|
||||
|
||||
typedef errno_t (*fd_context_read_fun)(FDContextMapNode *node, /**/
|
||||
void *buf, size_t nbytes, size_t *bytes_transferred);
|
||||
typedef errno_t (*fd_context_write_fun)(FDContextMapNode *node, /**/
|
||||
const void *buf, size_t nbytes, size_t *bytes_transferred);
|
||||
typedef errno_t (*fd_context_close_fun)(FDContextMapNode *node);
|
||||
|
||||
typedef struct {
|
||||
fd_context_read_fun read_fun;
|
||||
fd_context_write_fun write_fun;
|
||||
fd_context_close_fun close_fun;
|
||||
} FDContextVTable;
|
||||
|
||||
errno_t fd_context_default_read(FDContextMapNode *node, /**/
|
||||
void *buf, size_t nbytes, size_t *bytes_transferred);
|
||||
errno_t fd_context_default_write(FDContextMapNode *node, /**/
|
||||
void const *buf, size_t nbytes, size_t *bytes_transferred);
|
||||
|
||||
struct fd_context_map_node_ {
|
||||
RB_ENTRY(fd_context_map_node_) entry;
|
||||
int fd;
|
||||
int flags;
|
||||
union {
|
||||
EpollFDCtx epollfd;
|
||||
EventFDCtx eventfd;
|
||||
TimerFDCtx timerfd;
|
||||
SignalFDCtx signalfd;
|
||||
} ctx;
|
||||
FDContextVTable const *vtable;
|
||||
};
|
||||
|
||||
errno_t fd_context_map_node_destroy(FDContextMapNode *node);
|
||||
|
||||
/**/
|
||||
|
||||
typedef RB_HEAD(fd_context_map_, fd_context_map_node_) FDContextMap;
|
||||
|
||||
typedef struct {
|
||||
FDContextMap fd_context_map;
|
||||
pthread_mutex_t mutex;
|
||||
} EpollShimCtx;
|
||||
|
||||
extern EpollShimCtx epoll_shim_ctx;
|
||||
|
||||
FDContextMapNode *epoll_shim_ctx_create_node(EpollShimCtx *epoll_shim_ctx,
|
||||
errno_t *ec);
|
||||
FDContextMapNode *epoll_shim_ctx_find_node(EpollShimCtx *epoll_shim_ctx,
|
||||
int fd);
|
||||
FDContextMapNode *epoll_shim_ctx_remove_node(EpollShimCtx *epoll_shim_ctx,
|
||||
int fd);
|
||||
void epoll_shim_ctx_remove_node_explicit(EpollShimCtx *epoll_shim_ctx,
|
||||
FDContextMapNode *node);
|
||||
|
||||
/**/
|
||||
|
||||
int epoll_shim_close(int fd);
|
||||
ssize_t epoll_shim_read(int fd, void *buf, size_t nbytes);
|
||||
ssize_t epoll_shim_write(int fd, void const *buf, size_t nbytes);
|
||||
|
||||
#endif
|
1386
tpws/epoll-shim/src/epollfd_ctx.c
Normal file
1386
tpws/epoll-shim/src/epollfd_ctx.c
Normal file
File diff suppressed because it is too large
Load Diff
108
tpws/epoll-shim/src/epollfd_ctx.h
Normal file
108
tpws/epoll-shim/src/epollfd_ctx.h
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifndef EPOLLFD_CTX_H_
|
||||
#define EPOLLFD_CTX_H_
|
||||
|
||||
#include "fix.h"
|
||||
|
||||
#define SHIM_SYS_SHIM_HELPERS
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/tree.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
|
||||
struct registered_fds_node_;
|
||||
typedef struct registered_fds_node_ RegisteredFDsNode;
|
||||
|
||||
typedef enum {
|
||||
EOF_STATE_READ_EOF = 0x01,
|
||||
EOF_STATE_WRITE_EOF = 0x02,
|
||||
} EOFState;
|
||||
|
||||
typedef enum {
|
||||
NODE_TYPE_FIFO = 1,
|
||||
NODE_TYPE_SOCKET = 2,
|
||||
NODE_TYPE_KQUEUE = 3,
|
||||
NODE_TYPE_OTHER = 4,
|
||||
NODE_TYPE_POLL = 5,
|
||||
} NodeType;
|
||||
|
||||
struct registered_fds_node_ {
|
||||
RB_ENTRY(registered_fds_node_) entry;
|
||||
TAILQ_ENTRY(registered_fds_node_) pollfd_list_entry;
|
||||
|
||||
int fd;
|
||||
epoll_data_t data;
|
||||
|
||||
bool is_registered;
|
||||
|
||||
bool has_evfilt_read;
|
||||
bool has_evfilt_write;
|
||||
bool has_evfilt_except;
|
||||
|
||||
bool got_evfilt_read;
|
||||
bool got_evfilt_write;
|
||||
bool got_evfilt_except;
|
||||
|
||||
NodeType node_type;
|
||||
union {
|
||||
struct {
|
||||
bool readable;
|
||||
bool writable;
|
||||
} fifo;
|
||||
} node_data;
|
||||
int eof_state;
|
||||
bool pollpri_active;
|
||||
|
||||
uint16_t events;
|
||||
uint32_t revents;
|
||||
|
||||
bool is_edge_triggered;
|
||||
bool is_oneshot;
|
||||
|
||||
bool is_on_pollfd_list;
|
||||
int self_pipe[2];
|
||||
};
|
||||
|
||||
typedef TAILQ_HEAD(pollfds_list_, registered_fds_node_) PollFDList;
|
||||
typedef RB_HEAD(registered_fds_set_, registered_fds_node_) RegisteredFDsSet;
|
||||
|
||||
typedef struct {
|
||||
int kq; // non owning
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
PollFDList poll_fds;
|
||||
size_t poll_fds_size;
|
||||
|
||||
RegisteredFDsSet registered_fds;
|
||||
size_t registered_fds_size;
|
||||
|
||||
struct kevent *kevs;
|
||||
size_t kevs_length;
|
||||
|
||||
struct pollfd *pfds;
|
||||
size_t pfds_length;
|
||||
|
||||
pthread_mutex_t nr_polling_threads_mutex;
|
||||
pthread_cond_t nr_polling_threads_cond;
|
||||
unsigned long nr_polling_threads;
|
||||
|
||||
int self_pipe[2];
|
||||
} EpollFDCtx;
|
||||
|
||||
errno_t epollfd_ctx_init(EpollFDCtx *epollfd, int kq);
|
||||
errno_t epollfd_ctx_terminate(EpollFDCtx *epollfd);
|
||||
|
||||
void epollfd_ctx_fill_pollfds(EpollFDCtx *epollfd, struct pollfd *pfds);
|
||||
|
||||
errno_t epollfd_ctx_ctl(EpollFDCtx *epollfd, int op, int fd2,
|
||||
struct epoll_event *ev);
|
||||
errno_t epollfd_ctx_wait(EpollFDCtx *epollfd, struct epoll_event *ev, int cnt,
|
||||
int *actual_cnt);
|
||||
|
||||
#endif
|
31
tpws/epoll-shim/src/eventfd_ctx.h
Normal file
31
tpws/epoll-shim/src/eventfd_ctx.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef EVENTFD_CTX_H_
|
||||
#define EVENTFD_CTX_H_
|
||||
|
||||
#include "fix.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#define EVENTFD_CTX_FLAG_SEMAPHORE (1 << 0)
|
||||
|
||||
typedef struct {
|
||||
int kq_; // non owning
|
||||
int flags_;
|
||||
pthread_mutex_t mutex_;
|
||||
|
||||
bool is_signalled_;
|
||||
int self_pipe_[2]; // only used if EVFILT_USER is not available
|
||||
uint_least64_t counter_;
|
||||
} EventFDCtx;
|
||||
|
||||
errno_t eventfd_ctx_init(EventFDCtx *eventfd, int kq, unsigned int counter,
|
||||
int flags);
|
||||
errno_t eventfd_ctx_terminate(EventFDCtx *eventfd);
|
||||
|
||||
errno_t eventfd_ctx_write(EventFDCtx *eventfd, uint64_t value);
|
||||
errno_t eventfd_ctx_read(EventFDCtx *eventfd, uint64_t *value);
|
||||
|
||||
#endif
|
19
tpws/epoll-shim/src/fix.c
Normal file
19
tpws/epoll-shim/src/fix.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "fix.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
int ppoll(struct pollfd *fds, nfds_t nfds,const struct timespec *tmo_p, const sigset_t *sigmask)
|
||||
{
|
||||
// macos does not implement ppoll
|
||||
// this is a hacky ppoll shim. only for tpws which does not require sigmask
|
||||
if (sigmask)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
return poll(fds,nfds,tmo_p ? tmo_p->tv_sec*1000 + tmo_p->tv_nsec/1000000 : -1);
|
||||
}
|
||||
|
||||
#endif
|
20
tpws/epoll-shim/src/fix.h
Normal file
20
tpws/epoll-shim/src/fix.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _ERRNO_T_DEFINED
|
||||
#define _ERRNO_T_DEFINED
|
||||
typedef int errno_t;
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
#include <time.h>
|
||||
#include <signal.h>
|
||||
#include <poll.h>
|
||||
|
||||
struct itimerspec {
|
||||
struct timespec it_interval;
|
||||
struct timespec it_value;
|
||||
};
|
||||
int ppoll(struct pollfd *fds, nfds_t nfds,const struct timespec *tmo_p, const sigset_t *sigmask);
|
||||
|
||||
#endif
|
19
tpws/epoll-shim/src/signalfd_ctx.h
Normal file
19
tpws/epoll-shim/src/signalfd_ctx.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef SIGNALFD_CTX_H_
|
||||
#define SIGNALFD_CTX_H_
|
||||
|
||||
#include "fix.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
int kq; // non owning
|
||||
} SignalFDCtx;
|
||||
|
||||
errno_t signalfd_ctx_init(SignalFDCtx *signalfd, int kq, const sigset_t *sigs);
|
||||
errno_t signalfd_ctx_terminate(SignalFDCtx *signalfd);
|
||||
|
||||
errno_t signalfd_ctx_read(SignalFDCtx *signalfd, uint32_t *ident);
|
||||
|
||||
#endif
|
38
tpws/epoll-shim/src/timerfd_ctx.h
Normal file
38
tpws/epoll-shim/src/timerfd_ctx.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef TIMERFD_CTX_H_
|
||||
#define TIMERFD_CTX_H_
|
||||
|
||||
#include "fix.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct {
|
||||
int kq; // non owning
|
||||
int flags;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
int clockid;
|
||||
/*
|
||||
* Next expiration time, absolute (clock given by clockid).
|
||||
* If it_interval is != 0, it is a periodic timer.
|
||||
* If it_value is == 0, the timer is disarmed.
|
||||
*/
|
||||
struct itimerspec current_itimerspec;
|
||||
uint64_t nr_expirations;
|
||||
} TimerFDCtx;
|
||||
|
||||
errno_t timerfd_ctx_init(TimerFDCtx *timerfd, int kq, int clockid);
|
||||
errno_t timerfd_ctx_terminate(TimerFDCtx *timerfd);
|
||||
|
||||
errno_t timerfd_ctx_settime(TimerFDCtx *timerfd, int flags,
|
||||
struct itimerspec const *new, struct itimerspec *old);
|
||||
errno_t timerfd_ctx_gettime(TimerFDCtx *timerfd, struct itimerspec *cur);
|
||||
|
||||
errno_t timerfd_ctx_read(TimerFDCtx *timerfd, uint64_t *value);
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user