From dc8bb5d3f7454c948c622e51698e07afedde2fc3 Mon Sep 17 00:00:00 2001 From: Craig Edwards Date: Tue, 19 Nov 2024 01:46:52 +0000 Subject: [PATCH] fix kqueue to actually work on freebsd --- .cspell.json | 3 ++ src/dpp/socketengines/kqueue-facade.h | 69 +++++++++++++++++++++++++ src/dpp/socketengines/kqueue.cpp | 72 +++++++++------------------ 3 files changed, 96 insertions(+), 48 deletions(-) create mode 100644 src/dpp/socketengines/kqueue-facade.h diff --git a/.cspell.json b/.cspell.json index ebf8da3dab..82c89a6eb2 100644 --- a/.cspell.json +++ b/.cspell.json @@ -2,6 +2,9 @@ "version": "0.2", "language": "en-GB", "words": [ + "EVFILT", + "fflags", + "udata", "blurple", "featurable", "libdpp", diff --git a/src/dpp/socketengines/kqueue-facade.h b/src/dpp/socketengines/kqueue-facade.h new file mode 100644 index 0000000000..f11c62c0d2 --- /dev/null +++ b/src/dpp/socketengines/kqueue-facade.h @@ -0,0 +1,69 @@ +/************************************************************************************ + * + * D++, A Lightweight C++ library for Discord + * + * Copyright 2021 Craig Edwards and D++ contributors + * (https://github.com/brainboxdotcc/DPP/graphs/contributors) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ************************************************************************************/ +#pragma once + +/** + * Include actual kqueue + */ +#include +#include + +#if defined __NetBSD__ && __NetBSD_Version__ <= 999001400 + #define CAST_TYPE intptr_t +#else + #define CAST_TYPE void* +#endif + +#ifndef EV_ONESHOT + +#include + +/** + * This is a facade for kqueue(), a freebsd-only event mechanism. + * It is not documented here and only exists so that when editing the file on Linux + * the linter does not go nuts. For actual documentation of kqueue, + * see the relevant man pages. + */ + +int kqueue(); + +int kevent(int kq, const struct kevent *changelist, int number_changes, struct kevent *event_list, std::size_t number_events, const struct timespec *timeout); + +#define EV_SET(kev, ident, filter, flags, fflags, data, udata) + +struct kevent { + uintptr_t ident; /* identifier for this event */ + short filter; /* filter for event */ + uint16_t flags; /* action flags for kqueue */ + uint32_t fflags; /* filter flag value */ + intptr_t data; /* filter data value */ + void *udata; /* opaque user data identifier */ +}; + +#define EV_ADD 0x0001 +#define EV_DELETE 0x0002 +#define EV_ONESHOT 0x0010 +#define EV_EOF 0x8000 +#define EV_ERROR 0x4000 +#define EVFILT_READ (-1) +#define EVFILT_WRITE (-2) + +#endif diff --git a/src/dpp/socketengines/kqueue.cpp b/src/dpp/socketengines/kqueue.cpp index 05949a9388..635d97b598 100644 --- a/src/dpp/socketengines/kqueue.cpp +++ b/src/dpp/socketengines/kqueue.cpp @@ -24,24 +24,15 @@ #include #include #include -#include -#include #include #include - -#if defined __NetBSD__ && __NetBSD_Version__ <= 999001400 - #define CAST_TYPE intptr_t -#else - #define CAST_TYPE void* -#endif +#include "kqueue-facade.h" namespace dpp { struct socket_engine_kqueue : public socket_engine_base { int kqueue_handle{INVALID_SOCKET}; - unsigned int change_pos = 0; - std::vector change_list; std::vector ke_list; socket_engine_kqueue(const socket_engine_kqueue&) = delete; @@ -50,7 +41,6 @@ struct socket_engine_kqueue : public socket_engine_base { socket_engine_kqueue& operator=(socket_engine_kqueue&&) = delete; explicit socket_engine_kqueue(cluster* creator) : socket_engine_base(creator), kqueue_handle(kqueue()) { - change_list.resize(8); ke_list.resize(16); if (kqueue_handle == -1) { throw dpp::connection_exception("Failed to initialise kqueue()"); @@ -63,20 +53,12 @@ struct socket_engine_kqueue : public socket_engine_base { } } - struct kevent* get_change_kevent() - { - if (change_pos >= change_list.size()) { - change_list.resize(change_list.size() * 2); - } - return &change_list[change_pos++]; - } - void process_events() final { struct timespec ts{}; ts.tv_sec = 1; - int i = kevent(kqueue_handle, &change_list.front(), change_pos, &ke_list.front(), static_cast(ke_list.size()), &ts); - change_pos = 0; + //int i = kevent(kqueue_handle, &change_list.front(), change_pos, &ke_list.front(), static_cast(ke_list.size()), &ts); + int i = kevent(kqueue_handle, NULL, 0, &ke_list.front(), static_cast(ke_list.size()), &ts); if (i < 0) { return; @@ -90,7 +72,7 @@ struct socket_engine_kqueue : public socket_engine_base { } const short filter = kev.filter; - if (kev.flags & EV_EOF) { + if (kev.flags & EV_EOF || kev.flags & EV_ERROR) { eh->on_error(kev.ident, *eh, kev.fflags); continue; } @@ -105,29 +87,19 @@ struct socket_engine_kqueue : public socket_engine_base { } } - void set_event_write_flags(dpp::socket fd, socket_events* eh, uint8_t old_mask, uint8_t new_mask) - { - if (((new_mask & WANT_WRITE) != 0) && ((old_mask & WANT_WRITE) == 0)) - { - struct kevent* ke = get_change_kevent(); - EV_SET(ke, fd, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, static_cast(eh)); - } - else if (((old_mask & WANT_WRITE) != 0) && ((new_mask & WANT_WRITE) == 0)) - { - struct kevent* ke = get_change_kevent(); - EV_SET(ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr); - } - } - bool register_socket(const socket_events& e) final { bool r = socket_engine_base::register_socket(e); if (r) { - struct kevent* ke = get_change_kevent(); + struct kevent ke; socket_events* se = fds.find(e.fd)->second.get(); if ((se->flags & WANT_READ) != 0) { - EV_SET(ke, e.fd, EVFILT_READ, EV_ADD, 0, 0, static_cast(se)); + EV_SET(&ke, e.fd, EVFILT_READ, EV_ADD, 0, 0, static_cast(se)); + int i = kevent(kqueue_handle, &ke, 1, 0, 0, NULL); + } + if ((se->flags & WANT_WRITE) != 0) { + EV_SET(&ke, e.fd, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, static_cast(se)); + int i = kevent(kqueue_handle, &ke, 1, 0, 0, NULL); } - set_event_write_flags(e.fd, se, 0, e.flags); if (fds.size() * 2 > ke_list.size()) { ke_list.resize(fds.size() * 2); } @@ -138,12 +110,16 @@ struct socket_engine_kqueue : public socket_engine_base { bool update_socket(const socket_events& e) final { bool r = socket_engine_base::update_socket(e); if (r) { - struct kevent* ke = get_change_kevent(); socket_events* se = fds.find(e.fd)->second.get(); - if ((se->flags & WANT_READ) != 0) { - EV_SET(ke, e.fd, EVFILT_READ, EV_ADD, 0, 0, static_cast(se)); + struct kevent ke; + if ((e.flags & WANT_READ) != 0) { + EV_SET(&ke, e.fd, EVFILT_READ, EV_ADD, 0, 0, static_cast(se)); + int i = kevent(kqueue_handle, &ke, 1, 0, 0, NULL); + } + if ((e.flags & WANT_WRITE) != 0) { + EV_SET(&ke, e.fd, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, static_cast(se)); + int i = kevent(kqueue_handle, &ke, 1, 0, 0, NULL); } - set_event_write_flags(e.fd, se, 0, e.flags); if (fds.size() * 2 > ke_list.size()) { ke_list.resize(fds.size() * 2); } @@ -156,12 +132,12 @@ struct socket_engine_kqueue : public socket_engine_base { bool remove_socket(dpp::socket fd) final { bool r = socket_engine_base::remove_socket(fd); if (r) { - struct kevent* ke = get_change_kevent(); - EV_SET(ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr); - + struct kevent ke; + EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr); + int i = kevent(kqueue_handle, &ke, 1, 0, 0, NULL); // Then remove the read filter. - ke = get_change_kevent(); - EV_SET(ke, fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr); + EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr); + i = kevent(kqueue_handle, &ke, 1, 0, 0, NULL); } return r; }