Skip to content

Commit

Permalink
feat(breaking): 10.1 - socket engines, refactor, remove dpp::sync (#1334
Browse files Browse the repository at this point in the history
)
  • Loading branch information
braindigitalis authored Dec 15, 2024
2 parents e62aa6a + 56694b2 commit 265ccc0
Show file tree
Hide file tree
Showing 150 changed files with 4,013 additions and 6,636 deletions.
2 changes: 1 addition & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# TODO: Discuss about -readability-identifier-length, -readability-avoid-const-params-in-decls
Checks: "-*,bugprone-*,cert-*,clang-analyzer-*,concurrency-*,cppcoreguidelines-*,llvm-namespace-comment,modernize-*,performance-*,portability-*,readability-*,-bugprone-implicit-widening-of-multiplication-result, -bugprone-easily-swappable-parameters,-readability-identifier-length,-portability-restrict-system-includes,-modernize-use-trailing-return-type,-cppcoreguidelines-non-private-member-variables-in-classes,-readability-avoid-const-params-in-decls"
Checks: "-*,bugprone-*,cert-*,clang-analyzer-*,concurrency-*,cppcoreguidelines-*,llvm-namespace-comment,modernize-*,performance-*,portability-*,readability-*,-bugprone-implicit-widening-of-multiplication-result,-bugprone-easily-swappable-parameters,-readability-identifier-length,-portability-restrict-system-includes,-modernize-use-trailing-return-type,-cppcoreguidelines-non-private-member-variables-in-classes,-readability-avoid-const-params-in-decls,-cppcoreguidelines-owning-memory,-readability-function-cognitive-complexity,-cppcoreguidelines-avoid-do-while"
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
4 changes: 2 additions & 2 deletions buildtools/classes/Generator/CoroGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public function getCommentArray(): array
*/
public function saveHeader(string $content): void
{
$content .= "[[nodiscard]] async<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = \"1.1\", time_t request_timeout = 5);\n\n";
$content .= "[[nodiscard]] async<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = \"1.1\");\n\n";
file_put_contents('include/dpp/cluster_coro_calls.h', $content);
}

Expand All @@ -125,7 +125,7 @@ public function saveHeader(string $content): void
*/
public function saveCpp(string $cppcontent): void
{
$cppcontent .= "dpp::async<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol, time_t request_timeout) {\n\treturn async<http_request_completion_t>{ [&, this] <typename C> (C &&cc) { return this->request(url, method, std::forward<C>(cc), postdata, mimetype, headers, protocol, request_timeout); }};\n}
$cppcontent .= "dpp::async<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol) {\n\treturn async<http_request_completion_t>{ [&, this] <typename C> (C &&cc) { return this->request(url, method, std::forward<C>(cc), postdata, mimetype, headers, protocol); }};\n}
#endif
";
Expand Down
131 changes: 0 additions & 131 deletions buildtools/classes/Generator/SyncGenerator.php

This file was deleted.

17 changes: 17 additions & 0 deletions cmake/epoll.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
macro(CHECK_EPOLL VARIABLE)
if(UNIX)
if("${VARIABLE}" MATCHES "^${VARIABLE}$")
message(STATUS "Check if the system supports epoll")
include(CheckSymbolExists)
check_symbol_exists(epoll_create "sys/epoll.h" EPOLL_PROTOTYPE_EXISTS)

if(EPOLL_PROTOTYPE_EXISTS)
message(STATUS "Check if the system supports epoll - yes")
set(${VARIABLE} 1 CACHE INTERNAL "Result of CHECK_EPOLL" FORCE)
else(EPOLL_PROTOTYPE_EXISTS)
message(STATUS "Check if the system supports epoll - no")
set(${VARIABLE} "" CACHE INTERNAL "Result of CHECK_EPOLL" FORCE)
endif(EPOLL_PROTOTYPE_EXISTS)
endif("${VARIABLE}" MATCHES "^${VARIABLE}$")
endif(UNIX)
endmacro(CHECK_EPOLL)
17 changes: 17 additions & 0 deletions cmake/kqueue.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
macro(CHECK_KQUEUE VARIABLE)
if(UNIX)
if("${VARIABLE}" MATCHES "^${VARIABLE}$")
message(STATUS "Check if the system supports kqueue")
include(CheckSymbolExists)
check_symbol_exists(kqueue "sys/event.h" KQUEUE_PROTOTYPE_EXISTS)

if(KQUEUE_PROTOTYPE_EXISTS)
message(STATUS "Check if the system supports kqueue - yes")
set(${VARIABLE} 1 CACHE INTERNAL "Result of CHECK_KQUEUE" FORCE)
else(KQUEUE_PROTOTYPE_EXISTS)
message(STATUS "Check if the system supports kqueue - no")
set(${VARIABLE} "" CACHE INTERNAL "Result of CHECK_KQUEUE" FORCE)
endif(KQUEUE_PROTOTYPE_EXISTS)
endif("${VARIABLE}" MATCHES "^${VARIABLE}$")
endif(UNIX)
endmacro(CHECK_KQUEUE)
82 changes: 30 additions & 52 deletions docpages/advanced_reference/thread_model.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
digraph "Thread Model" {
graph [ranksep=1];
node [colorscheme="blues9",fontname="helvetica"];
"Discord Events" -> "Your Program"

"Your Program" [style=filled, color=1, shape=rect]
"Cluster" [style=filled, color=1, shape=rect]

Expand All @@ -14,68 +12,48 @@ digraph "Thread Model" {
color=lightgrey;
node [style=filled,color=2]
"Your Program"
"Cluster"
label = "User Code";
"Your Program" -> "Cluster"
label = "(1)";
}

subgraph cluster_0 {
subgraph cluster_3 {
style=filled;
color=lightgrey;
node [style=filled,color=4]
"Shard 1" [style=filled, color=4]
"Shard 2"
"Shard 3..."
label = "Shards (Each is a thread, one per 2500 Discord guilds)";
node [style=filled,color=2]
"Cluster" -> "Event Loop"
"Event Loop" -> "HTTP(S) requests"
"Event Loop" -> "Voice Sessions"
"Event Loop" -> "Shards"
"Shards" -> "Websocket Events"
label = "(2)";
}

subgraph cluster_1 {
style=filled
color=lightgrey;
node [style=filled,color=4]
"REST Requests"
"Request In Queue 1"
"Request In Queue 2"
"Request In Queue 3..."
"Request Out Queue"
label = "REST Requests (Each in queue, and the out queue, are threads)"
}

subgraph cluster_3 {
style=filled
subgraph cluster_0 {
style=filled;
color=lightgrey;
node [style=filled,color=4]
"Discord Events" [style=filled,color=4]
"User Callback Functions"
label = "Events and Callbacks"
"Voice Sessions" -> "Websocket Events"
"HTTP(S) requests" -> "Thread Pool (4..n threads)"
"Websocket Events" -> "Thread Pool (4..n threads)"
"Thread Pool (4..n threads)"
label = "(3)";
}

"Cluster" [shape=rect]
"REST Requests" [shape=rect]
"Request In Queue 1" [shape=rect]
"Request In Queue 2" [shape=rect]
"Request In Queue 3..." [shape=rect]
"Shard 1" [shape=rect]
"Shard 2" [shape=rect]
"Shard 3..." [shape=rect]
"Request Out Queue" [shape=rect]
"Discord Events" [shape=rect]
"User Callback Functions" [shape=rect]
"Thread Pool (4..n threads)" [shape=rect]
"HTTP(S) requests" [shape=rect]
"Shards" [shape=rect]
"Websocket Events" [shape=rect]
"Event Loop" [shape=rect]
"Voice Sessions" [shape=rect]

"Cluster" -> "REST Requests"
"Shard 1" -> "Discord Events"
"Shard 2" -> "Discord Events"
"Shard 3..." -> "Discord Events"
"Your Program" -> "Cluster"
"Cluster" -> "Shard 1"
"Cluster" -> "Shard 2"
"Cluster" -> "Shard 3..."
"REST Requests" -> "Request In Queue 1"
"REST Requests" -> "Request In Queue 2"
"REST Requests" -> "Request In Queue 3..."
"Request In Queue 1" -> "Request Out Queue"
"Request In Queue 2" -> "Request Out Queue"
"Request In Queue 3..." -> "Request Out Queue"
"Request Out Queue" -> "User Callback Functions"
"User Callback Functions" -> "Your Program"
}
\enddot

## Details

1. User Code - No assumptions are made about how your program threads, if at all.
2. The event loop manages all socket IO for the cluster. If you start the cluster with dpp::st_return this will run under its own thread, otherwise if you use dpp::st_wait it will run in the same thread as the caller of the dpp::cluster::start method.
The event loop will be either poll, epoll or kqueue based depending on your system capabilities. You should always start a cluster after forking, if your program forks, as various types of IO loop cannot be inherited by a forked process.
3. Set thread pool size via cluster constructor. Thread pool uses a priority queue and defaults in size to half the system concurrency value. Every callback or completed coroutine ends up executing here. The minimum concurrency of this pool is 4.
2 changes: 1 addition & 1 deletion docpages/example_code/coro_awaiting_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ int main() {
);
co_await event.co_reply(m);

dpp::button_click_t click_event = co_await event.from->creator->on_button_click.when(
dpp::button_click_t click_event = co_await event.owner->on_button_click.when(
// Note!! Due to a bug in g++11 and g++12, id must be captured as a reference here or the compiler will destroy it twice. This is fixed in g++13
[&id] (dpp::button_click_t const &b) {
return b.custom_id == id;
Expand Down
4 changes: 2 additions & 2 deletions docpages/example_code/coro_expiring_buttons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ int main() {
co_await event.co_reply(m);

auto result = co_await dpp::when_any{ // Whichever completes first...
event.from->creator->on_button_click.when([&id](const dpp::button_click_t &b) { // Button clicked
event.owner->on_button_click.when([&id](const dpp::button_click_t &b) { // Button clicked
return b.custom_id == id;
}),
event.from->creator->co_sleep(5) // Or sleep 5 seconds
event.owner->co_sleep(5) // Or sleep 5 seconds
};
// Note!! Due to a bug in g++11 and g++12, id must be captured as a reference above or the compiler will destroy it twice. This is fixed in g++13
if (result.index() == 0) { // Awaitable #0 completed first, that is the button click event
Expand Down
2 changes: 1 addition & 1 deletion docpages/example_code/coro_intro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ int main() {
bot.on_slashcommand([](const dpp::slashcommand_t& event) -> dpp::task<void> {
if (event.command.get_command_name() == "file") {
/* Request the image from the URL specified and co_await the response */
dpp::http_request_completion_t result = co_await event.from->creator->co_request("https://dpp.dev/DPP-Logo.png", dpp::m_get);
dpp::http_request_completion_t result = co_await event.owner->co_request("https://dpp.dev/DPP-Logo.png", dpp::m_get);

/* Create a message and attach the image on success */
dpp::message msg(event.command.channel_id, "This is my new attachment:");
Expand Down
2 changes: 1 addition & 1 deletion docpages/example_code/coro_simple_commands1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ int main() {

bot.on_slashcommand([](const dpp::slashcommand_t& event) -> dpp::task<void> {
if (event.command.get_command_name() == "addemoji") {
dpp::cluster *cluster = event.from->creator;
dpp::cluster *cluster = event.owner;
// Retrieve parameter values
dpp::snowflake file_id = std::get<dpp::snowflake>(event.get_parameter("file"));
std::string emoji_name = std::get<std::string>(event.get_parameter("name"));
Expand Down
4 changes: 2 additions & 2 deletions docpages/example_code/coro_simple_commands2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ int main() {
}
}
// Finally if everything else failed, request API
dpp::confirmation_callback_t confirmation = co_await event.from->creator->co_guild_get_member(event.command.guild_id, user_id);
dpp::confirmation_callback_t confirmation = co_await event.owner->co_guild_get_member(event.command.guild_id, user_id);
if (confirmation.is_error()) {
co_return std::nullopt; // Member not found, return empty
} else {
Expand All @@ -53,7 +53,7 @@ int main() {

std::string avatar_url = member->get_avatar_url(512);
if (avatar_url.empty()) { // Member does not have a custom avatar for this server, get their user avatar
dpp::confirmation_callback_t confirmation = co_await event.from->creator->co_user_get_cached(member->user_id);
dpp::confirmation_callback_t confirmation = co_await event.owner->co_user_get_cached(member->user_id);
if (confirmation.is_error()) {
// Wait for the thinking response to arrive to make sure we can edit
co_await thinking;
Expand Down
4 changes: 2 additions & 2 deletions docpages/example_code/join_voice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ int main() {
dpp::guild* g = dpp::find_guild(event.command.guild_id);

/* Get the voice channel that the bot is currently in from this server (will return nullptr if we're not in a voice channel!) */
auto current_vc = event.from->get_voice(event.command.guild_id);
auto current_vc = event.from()->get_voice(event.command.guild_id);

bool join_vc = true;

Expand All @@ -38,7 +38,7 @@ int main() {
/* We are on a different voice channel. We should leave it, then join the new one
* by falling through to the join_vc branch below.
*/
event.from->disconnect_voice(event.command.guild_id);
event.from()->disconnect_voice(event.command.guild_id);

join_vc = true;
}
Expand Down
2 changes: 1 addition & 1 deletion docpages/example_code/mp3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ int main() {
event.reply("Joined your channel!");
} else if (event.command.get_command_name() == "mp3") {
/* Get the voice channel the bot is in, in this current guild. */
dpp::voiceconn* v = event.from->get_voice(event.command.guild_id);
dpp::voiceconn* v = event.from()->get_voice(event.command.guild_id);

/* If the voice channel was invalid, or there is an issue with it, then tell the user. */
if (!v || !v->voiceclient || !v->voiceclient->is_ready()) {
Expand Down
Loading

0 comments on commit 265ccc0

Please sign in to comment.