Skip to content

Commit

Permalink
fix kqueue to actually work on freebsd
Browse files Browse the repository at this point in the history
  • Loading branch information
braindigitalis committed Nov 19, 2024
1 parent 1169268 commit dc8bb5d
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 48 deletions.
3 changes: 3 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
"version": "0.2",
"language": "en-GB",
"words": [
"EVFILT",
"fflags",
"udata",
"blurple",
"featurable",
"libdpp",
Expand Down
69 changes: 69 additions & 0 deletions src/dpp/socketengines/kqueue-facade.h
Original file line number Diff line number Diff line change
@@ -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 <sys/event.h>
#include <sys/sysctl.h>

#if defined __NetBSD__ && __NetBSD_Version__ <= 999001400
#define CAST_TYPE intptr_t
#else
#define CAST_TYPE void*
#endif

#ifndef EV_ONESHOT

#include <cstdint>

/**
* 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
72 changes: 24 additions & 48 deletions src/dpp/socketengines/kqueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,15 @@
#include <memory>
#include <vector>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/sysctl.h>
#include <unistd.h>
#include <dpp/cluster.h>

#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<struct kevent> change_list;
std::vector<struct kevent> ke_list;

socket_engine_kqueue(const socket_engine_kqueue&) = delete;
Expand All @@ -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()");
Expand All @@ -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<int>(ke_list.size()), &ts);
change_pos = 0;
//int i = kevent(kqueue_handle, &change_list.front(), change_pos, &ke_list.front(), static_cast<int>(ke_list.size()), &ts);
int i = kevent(kqueue_handle, NULL, 0, &ke_list.front(), static_cast<int>(ke_list.size()), &ts);

if (i < 0) {
return;
Expand All @@ -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;
}
Expand All @@ -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<CAST_TYPE>(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<CAST_TYPE>(se));
EV_SET(&ke, e.fd, EVFILT_READ, EV_ADD, 0, 0, static_cast<CAST_TYPE>(se));
int i = kevent(kqueue_handle, &ke, 1, 0, 0, NULL);

Check notice on line 97 in src/dpp/socketengines/kqueue.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/dpp/socketengines/kqueue.cpp#L97

Variable 'i' is assigned a value that is never used.
}
if ((se->flags & WANT_WRITE) != 0) {
EV_SET(&ke, e.fd, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, static_cast<CAST_TYPE>(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);
}
Expand All @@ -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();

Check notice on line 113 in src/dpp/socketengines/kqueue.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/dpp/socketengines/kqueue.cpp#L113

Variable 'se' is assigned a value that is never used.
if ((se->flags & WANT_READ) != 0) {
EV_SET(ke, e.fd, EVFILT_READ, EV_ADD, 0, 0, static_cast<CAST_TYPE>(se));
struct kevent ke;
if ((e.flags & WANT_READ) != 0) {
EV_SET(&ke, e.fd, EVFILT_READ, EV_ADD, 0, 0, static_cast<CAST_TYPE>(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<CAST_TYPE>(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);
}
Expand All @@ -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);

Check notice on line 140 in src/dpp/socketengines/kqueue.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/dpp/socketengines/kqueue.cpp#L140

Variable 'i' is assigned a value that is never used.

Check notice on line 140 in src/dpp/socketengines/kqueue.cpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/dpp/socketengines/kqueue.cpp#L140

Variable 'i' is reassigned a value before the old one has been used.
}
return r;
}
Expand Down

0 comments on commit dc8bb5d

Please sign in to comment.