From a49389d508555bf82b2f8178a0b28f0371f7a631 Mon Sep 17 00:00:00 2001 From: Eric Mertens Date: Sun, 26 Jan 2025 19:35:56 -0800 Subject: [PATCH] explicitly tear down slots --- CMakeLists.txt | 4 +- bot.cpp | 37 ++++++++++++--- bot.hpp | 12 +++-- self_thread.cpp => client.cpp | 51 +++++++++++---------- self_thread.hpp => client.hpp | 11 +++-- connection.cpp | 7 +++ main.cpp | 31 +++++++++---- registration_thread.cpp => registration.cpp | 48 +++++++++---------- registration_thread.hpp => registration.hpp | 17 ++++--- 9 files changed, 137 insertions(+), 81 deletions(-) rename self_thread.cpp => client.cpp (80%) rename self_thread.hpp => client.hpp (90%) rename registration_thread.cpp => registration.cpp (73%) rename registration_thread.hpp => registration.hpp (63%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fb8d07a..cfaccba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,9 +46,9 @@ add_executable(xbot connection.cpp irc_coroutine.cpp ircmsg.cpp - registration_thread.cpp + registration.cpp sasl_mechanism.cpp - self_thread.cpp + client.cpp settings.cpp snote.cpp ) diff --git a/bot.cpp b/bot.cpp index 6cbc4d1..824f249 100644 --- a/bot.cpp +++ b/bot.cpp @@ -2,7 +2,7 @@ #include -auto Bot::start(std::shared_ptr self) -> std::shared_ptr +auto Bot::start(std::shared_ptr self) -> std::shared_ptr { const auto thread = std::make_shared(std::move(self)); @@ -22,18 +22,38 @@ auto Bot::process_command(std::string_view message, const IrcMsg &msg) -> void message = message.substr(cmdstart); if (not message.starts_with(command_prefix_)) return; + message = message.substr(1); // discard the prefix - auto cmdend = message.find_first_of(' ', 1); + auto cmdend = message.find(' '); std::string_view command = cmdend == message.npos ? message : message.substr(0, cmdend); - - std::string_view arguments = cmdend == message.npos ? std::string_view{} : message.substr(cmdend + 1); - BOOST_LOG_TRIVIAL(debug) << "COMMAND: " << command << " -- [" << arguments << "]"; + std::string_view oper; + std::string_view account; + for (auto [key, value] : msg.tags) + { + if (key == "account") + { + account = value; + } + else if (key == "operator") + { + oper = value; + } + } + + sig_command({ + .source = msg.args[0], + .target = msg.args[1], + .oper = oper, + .account = account, + .command = command, + .arguments = arguments + }); } auto Bot::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void @@ -52,4 +72,9 @@ auto Bot::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void process_command(message.substr(colon+1), msg); } } -} \ No newline at end of file +} + +auto Bot::shutdown() -> void +{ + sig_command.disconnect_all_slots(); +} diff --git a/bot.hpp b/bot.hpp index 3e03319..d28448d 100644 --- a/bot.hpp +++ b/bot.hpp @@ -1,6 +1,6 @@ #pragma once -#include "self_thread.hpp" +#include "client.hpp" #include @@ -18,15 +18,19 @@ struct Command struct Bot : std::enable_shared_from_this { - std::shared_ptr self_; + std::shared_ptr self_; char command_prefix_; - Bot(std::shared_ptr self) + boost::signals2::signal sig_command; + + Bot(std::shared_ptr self) : self_{std::move(self)} , command_prefix_{'!'} {} auto on_ircmsg(IrcCommand, const IrcMsg &) -> void; auto process_command(std::string_view message, const IrcMsg &msg) -> void; - static auto start(std::shared_ptr) -> std::shared_ptr; + static auto start(std::shared_ptr) -> std::shared_ptr; + + auto shutdown() -> void; }; diff --git a/self_thread.cpp b/client.cpp similarity index 80% rename from self_thread.cpp rename to client.cpp index 43a24ad..b40fa20 100644 --- a/self_thread.cpp +++ b/client.cpp @@ -1,4 +1,4 @@ -#include "self_thread.hpp" +#include "client.hpp" #include "connection.hpp" @@ -9,17 +9,17 @@ using namespace std::literals; -auto SelfThread::on_welcome(const IrcMsg &irc) -> void +auto Client::on_welcome(const IrcMsg &irc) -> void { nickname_ = irc.args[0]; } -auto SelfThread::on_registered() -> void +auto Client::on_registered() -> void { - connection_.send_join("#lobby"); + sig_registered(); } -auto SelfThread::on_nick(const IrcMsg &irc) -> void +auto Client::on_nick(const IrcMsg &irc) -> void { if (is_my_mask(irc.source)) { @@ -27,12 +27,12 @@ auto SelfThread::on_nick(const IrcMsg &irc) -> void } } -auto SelfThread::on_umodeis(const IrcMsg &irc) -> void +auto Client::on_umodeis(const IrcMsg &irc) -> void { mode_ = irc.args[1]; } -auto SelfThread::on_join(const IrcMsg &irc) -> void +auto Client::on_join(const IrcMsg &irc) -> void { if (is_my_mask(irc.source)) { @@ -40,7 +40,7 @@ auto SelfThread::on_join(const IrcMsg &irc) -> void } } -auto SelfThread::on_kick(const IrcMsg &irc) -> void +auto Client::on_kick(const IrcMsg &irc) -> void { if (is_my_nick(irc.args[1])) { @@ -48,7 +48,7 @@ auto SelfThread::on_kick(const IrcMsg &irc) -> void } } -auto SelfThread::on_part(const IrcMsg &irc) -> void +auto Client::on_part(const IrcMsg &irc) -> void { if (is_my_mask(irc.source)) { @@ -56,7 +56,7 @@ auto SelfThread::on_part(const IrcMsg &irc) -> void } } -auto SelfThread::on_mode(const IrcMsg &irc) -> void +auto Client::on_mode(const IrcMsg &irc) -> void { if (is_my_nick(irc.args[0])) { @@ -90,7 +90,7 @@ auto SelfThread::on_mode(const IrcMsg &irc) -> void } } -auto SelfThread::on_isupport(const IrcMsg &msg) -> void +auto Client::on_isupport(const IrcMsg &msg) -> void { const auto hi = msg.args.size() - 1; for (int i = 1; i < hi; ++i) @@ -117,9 +117,9 @@ auto SelfThread::on_isupport(const IrcMsg &msg) -> void } } -auto SelfThread::start(Connection &connection) -> std::shared_ptr +auto Client::start(Connection &connection) -> std::shared_ptr { - auto thread = std::make_shared(connection); + auto thread = std::make_shared(connection); connection.sig_ircmsg.connect([thread](auto cmd, auto &msg) { switch (cmd) @@ -163,38 +163,38 @@ auto SelfThread::start(Connection &connection) -> std::shared_ptr return thread; } -auto SelfThread::get_my_nickname() const -> const std::string & +auto Client::get_my_nickname() const -> const std::string & { return nickname_; } -auto SelfThread::get_my_mode() const -> const std::string & +auto Client::get_my_mode() const -> const std::string & { return mode_; } -auto SelfThread::get_my_channels() const -> const std::unordered_set & +auto Client::get_my_channels() const -> const std::unordered_set & { return channels_; } -auto SelfThread::is_my_nick(std::string_view nick) const -> bool +auto Client::is_my_nick(std::string_view nick) const -> bool { return casemap_compare(nick, nickname_) == 0; } -auto SelfThread::is_my_mask(std::string_view mask) const -> bool +auto Client::is_my_mask(std::string_view mask) const -> bool { const auto bang = mask.find('!'); return bang != std::string_view::npos && nickname_ == mask.substr(0, bang); } -auto SelfThread::is_channel(std::string_view name) const -> bool +auto Client::is_channel(std::string_view name) const -> bool { return not name.empty() && channel_prefix_.find(name[0]) != channel_prefix_.npos; } -auto SelfThread::on_authenticate(const std::string_view body) -> void +auto Client::on_authenticate(const std::string_view body) -> void { if (not sasl_mechanism_) { @@ -216,7 +216,7 @@ auto SelfThread::on_authenticate(const std::string_view body) -> void } } -auto SelfThread::start_sasl(std::unique_ptr mechanism) -> void +auto Client::start_sasl(std::unique_ptr mechanism) -> void { if (sasl_mechanism_) { @@ -248,7 +248,7 @@ static auto casemap_impl(std::string_view str) -> std::string return result; } -auto SelfThread::casemap(std::string_view str) const -> std::string +auto Client::casemap(std::string_view str) const -> std::string { switch (casemap_) { case Casemap::Ascii: return casemap_impl(str); @@ -272,11 +272,16 @@ static auto casemap_compare_impl(std::string_view lhs, std::string_view rhs) -> return 0; } -auto SelfThread::casemap_compare(std::string_view lhs, std::string_view rhs) const -> int +auto Client::casemap_compare(std::string_view lhs, std::string_view rhs) const -> int { switch (casemap_) { case Casemap::Ascii: return casemap_compare_impl(lhs, rhs); case Casemap::Rfc1459: return casemap_compare_impl(lhs, rhs); case Casemap::Rfc1459_Strict: return casemap_compare_impl(lhs, rhs); } +} + +auto Client::shutdown() -> void +{ + sig_registered.disconnect_all_slots(); } \ No newline at end of file diff --git a/self_thread.hpp b/client.hpp similarity index 90% rename from self_thread.hpp rename to client.hpp index a9cda10..d44de65 100644 --- a/self_thread.hpp +++ b/client.hpp @@ -20,7 +20,7 @@ enum class Casemap * @brief Thread to track this connection's identity, and IRC state. * */ -class SelfThread +class Client { Connection &connection_; @@ -47,13 +47,16 @@ class SelfThread auto on_registered() -> void; public: - SelfThread(Connection &connection) + boost::signals2::signal sig_registered; + + Client(Connection &connection) : connection_{connection} , casemap_{Casemap::Rfc1459} , channel_prefix_{"#&"} { } - static auto start(Connection &) -> std::shared_ptr; + + static auto start(Connection &) -> std::shared_ptr; auto start_sasl(std::unique_ptr mechanism) -> void; @@ -72,4 +75,6 @@ public: auto casemap(std::string_view) const -> std::string; auto casemap_compare(std::string_view, std::string_view) const -> int; + + auto shutdown() -> void; }; diff --git a/connection.cpp b/connection.cpp index 9c4fb7e..b5ad1e7 100644 --- a/connection.cpp +++ b/connection.cpp @@ -477,5 +477,12 @@ auto Connection::start(ConnectSettings settings) -> void } self->sig_disconnect(); + + // Disconnect all slots to avoid circular references + self->sig_connect.disconnect_all_slots(); + self->sig_ircmsg.disconnect_all_slots(); + self->sig_disconnect.disconnect_all_slots(); + self->sig_snote.disconnect_all_slots(); + self->sig_authenticate.disconnect_all_slots(); }); } \ No newline at end of file diff --git a/main.cpp b/main.cpp index 9936887..f6c0c84 100644 --- a/main.cpp +++ b/main.cpp @@ -10,18 +10,18 @@ #include #include "bot.hpp" -#include "registration_thread.hpp" -#include "self_thread.hpp" +#include "client.hpp" +#include "registration.hpp" -using namespace std::chrono_literals; +using namespace std::literals; auto start(boost::asio::io_context &io, const Settings &settings) -> void { const auto connection = std::make_shared(io); + const auto client = Client::start(*connection); + Registration::start(*connection, settings, client); - const auto selfThread = SelfThread::start(*connection); - RegistrationThread::start(*connection, settings, selfThread); - Bot::start(selfThread); + const auto bot = Bot::start(client); connection->sig_snote.connect([](auto &match) { std::cout << "SNOTE " << static_cast(match.get_tag()) << std::endl; @@ -31,16 +31,27 @@ auto start(boost::asio::io_context &io, const Settings &settings) -> void } }); - connection->sig_disconnect.connect_extended( - [&io, &settings](auto &slot) { - slot.disconnect(); + client->sig_registered.connect_extended([connection](auto &slot) { + slot.disconnect(); + connection->send_join("##glguy"sv); + }); + + connection->sig_disconnect.connect( + [&io, &settings, client, bot]() { + client->shutdown(); + bot->shutdown(); + auto timer = std::make_shared(io); timer->expires_after(5s); timer->async_wait([&io, &settings, timer](auto) { start(io, settings); }); } ); - connection->start(ConnectSettings{ + bot->sig_command.connect([connection](const Command &cmd) { + std::cout << "COMMAND " << cmd.command << " from " << cmd.account << std::endl; + }); + + connection->start({ .tls = settings.use_tls, .host = settings.host, .port = settings.service, diff --git a/registration_thread.cpp b/registration.cpp similarity index 73% rename from registration_thread.cpp rename to registration.cpp index bdc6086..cc2707d 100644 --- a/registration_thread.cpp +++ b/registration.cpp @@ -1,4 +1,4 @@ -#include "registration_thread.hpp" +#include "registration.hpp" #include "connection.hpp" #include "ircmsg.hpp" @@ -8,10 +8,10 @@ #include #include -RegistrationThread::RegistrationThread( +Registration::Registration( Connection &connection, const Settings &settings, - std::shared_ptr self + std::shared_ptr self ) : connection_{connection} , settings_{settings} @@ -19,9 +19,11 @@ RegistrationThread::RegistrationThread( { } -auto RegistrationThread::on_connect() -> void +auto Registration::on_connect() -> void { connection_.send_cap_ls(); + listen_for_cap_ls(); + if (not settings_.password.empty()) { connection_.send_pass(settings_.password); @@ -30,7 +32,7 @@ auto RegistrationThread::on_connect() -> void connection_.send_nick(settings_.nickname); } -auto RegistrationThread::send_req() -> void +auto Registration::send_req() -> void { std::string request; std::vector want{ @@ -77,7 +79,7 @@ auto RegistrationThread::send_req() -> void } } -auto RegistrationThread::on_msg_cap_ack(const IrcMsg &msg) -> void +auto Registration::on_msg_cap_ack(const IrcMsg &msg) -> void { auto in = std::istringstream{std::string{msg.args[2]}}; std::for_each( @@ -89,16 +91,16 @@ auto RegistrationThread::on_msg_cap_ack(const IrcMsg &msg) -> void ); if (outstanding.empty()) { - message_handle_.disconnect(); if (settings_.sasl_mechanism.empty()) { + slot_.disconnect(); connection_.send_cap_end(); } else { self_->start_sasl(std::make_unique(settings_.sasl_authcid, settings_.sasl_authzid, settings_.sasl_password)); - connection_.sig_ircmsg.connect_extended([thread = shared_from_this()](auto &slot, auto cmd, auto &msg) { + slot_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](auto cmd, auto &msg) { switch (cmd) { default: @@ -106,14 +108,14 @@ auto RegistrationThread::on_msg_cap_ack(const IrcMsg &msg) -> void case IrcCommand::RPL_SASLSUCCESS: case IrcCommand::ERR_SASLFAIL: thread->connection_.send_cap_end(); - slot.disconnect(); + thread->slot_.disconnect(); } }); } } } -auto RegistrationThread::on_msg_cap_ls(const IrcMsg &msg) -> void +auto Registration::on_msg_cap_ls(const IrcMsg &msg) -> void { const std::string_view *kvs; bool last; @@ -153,32 +155,30 @@ auto RegistrationThread::on_msg_cap_ls(const IrcMsg &msg) -> void if (last) { - message_handle_.disconnect(); + slot_.disconnect(); send_req(); } } -auto RegistrationThread::start( +auto Registration::start( Connection &connection, const Settings &settings, - std::shared_ptr self -) -> std::shared_ptr + std::shared_ptr self +) -> std::shared_ptr { - const auto thread = std::make_shared(connection, std::move(settings), std::move(self)); + const auto thread = std::make_shared(connection, std::move(settings), std::move(self)); - thread->listen_for_cap_ls(); - - thread->connect_handle_ = connection.sig_connect.connect([thread]() { - thread->connect_handle_.disconnect(); + thread->slot_ = connection.sig_connect.connect([thread]() { + thread->slot_.disconnect(); thread->on_connect(); }); return thread; } -auto RegistrationThread::listen_for_cap_ack() -> void +auto Registration::listen_for_cap_ack() -> void { - message_handle_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](IrcCommand cmd, const IrcMsg &msg) { + slot_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](IrcCommand cmd, const IrcMsg &msg) { if (IrcCommand::CAP == cmd && msg.args.size() >= 2 && "ACK" == msg.args[1]) { thread->on_msg_cap_ack(msg); @@ -186,9 +186,9 @@ auto RegistrationThread::listen_for_cap_ack() -> void }); } -auto RegistrationThread::listen_for_cap_ls() -> void +auto Registration::listen_for_cap_ls() -> void { - message_handle_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](IrcCommand cmd, const IrcMsg &msg) { + slot_ = connection_.sig_ircmsg.connect([thread = shared_from_this()](IrcCommand cmd, const IrcMsg &msg) { if (IrcCommand::CAP == cmd && msg.args.size() >= 2 && "LS" == msg.args[1]) { thread->on_msg_cap_ls(msg); @@ -196,7 +196,7 @@ auto RegistrationThread::listen_for_cap_ls() -> void else if (IrcCommand::RPL_WELCOME == cmd) { // Server doesn't support CAP negotiation - thread->message_handle_.disconnect(); + thread->slot_.disconnect(); } }); } diff --git a/registration_thread.hpp b/registration.hpp similarity index 63% rename from registration_thread.hpp rename to registration.hpp index b808460..ce5746b 100644 --- a/registration_thread.hpp +++ b/registration.hpp @@ -1,7 +1,7 @@ #pragma once #include "connection.hpp" -#include "self_thread.hpp" +#include "client.hpp" #include "settings.hpp" #include @@ -9,17 +9,16 @@ #include #include -class RegistrationThread : public std::enable_shared_from_this +class Registration : public std::enable_shared_from_this { Connection &connection_; const Settings &settings_; - std::shared_ptr self_; + std::shared_ptr self_; std::unordered_map caps; std::unordered_set outstanding; - boost::signals2::scoped_connection connect_handle_; - boost::signals2::scoped_connection message_handle_; + boost::signals2::scoped_connection slot_; auto on_connect() -> void; auto send_req() -> void; @@ -30,15 +29,15 @@ class RegistrationThread : public std::enable_shared_from_this void; public: - RegistrationThread( + Registration( Connection &connection_, const Settings &, - std::shared_ptr self + std::shared_ptr self ); static auto start( Connection &connection, const Settings &, - std::shared_ptr self - ) -> std::shared_ptr; + std::shared_ptr self + ) -> std::shared_ptr; };