mirror of
https://github.com/bol-van/zapret.git
synced 2024-11-26 20:20:53 +03:00
282 lines
5.4 KiB
C
282 lines
5.4 KiB
C
|
#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;
|
||
|
}
|