From d69364ec196c9f2bd79ec9778f2d60f6541c0eec Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Tue, 20 Aug 2024 19:07:31 +0200 Subject: [PATCH] Fix crash on IRC disconnect due to maintenance Reworked the (dis)connect handling --- plugins/twitch/chat-connection.cpp | 71 +++++++++++++++++++----------- plugins/twitch/chat-connection.hpp | 7 ++- 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/plugins/twitch/chat-connection.cpp b/plugins/twitch/chat-connection.cpp index 2416b25a..97ec922a 100644 --- a/plugins/twitch/chat-connection.cpp +++ b/plugins/twitch/chat-connection.cpp @@ -9,11 +9,13 @@ namespace advss { +using namespace std::chrono_literals; + using websocketpp::lib::placeholders::_1; using websocketpp::lib::placeholders::_2; using websocketpp::lib::bind; -static const int reconnectDelay = 15; +static const auto reconnectDelay = 15s; /* ------------------------------------------------------------------------- */ @@ -390,15 +392,14 @@ TwitchChatConnection::GetChatConnection(const TwitchToken &token_, void TwitchChatConnection::ConnectThread() { - while (!_disconnect) { - std::unique_lock lock(_waitMtx); + while (!_stop) { + _state = State::CONNECTING; _client.reset(); - _connected = true; websocketpp::lib::error_code ec; websocketpp::client:: connection_ptr con = _client.get_connection(_url, ec); if (ec) { - blog(LOG_INFO, "Twitch TwitchChatConnection failed: %s", + blog(LOG_INFO, "TwitchChatConnection failed: %s", ec.message().c_str()); } else { _client.connect(con); @@ -406,52 +407,63 @@ void TwitchChatConnection::ConnectThread() _client.run(); } + if (_stop) { + break; + } + blog(LOG_INFO, - "Twitch TwitchChatConnection trying to reconnect to in %d seconds.", - reconnectDelay); + "TwitchChatConnection trying to reconnect to in %ld seconds.", + (long int)reconnectDelay.count()); + std::unique_lock lock(_waitMtx); _cv.wait_for(lock, std::chrono::seconds(reconnectDelay)); } - _connected = false; + _state = State::DISCONNECTED; } void TwitchChatConnection::Connect() { std::lock_guard lock(_connectMtx); - if (_connected) { + if (_state == State::CONNECTING || _state == State::CONNECTED) { vblog(LOG_INFO, "Twitch TwitchChatConnection connect already in progress"); return; } - _disconnect = true; + if (_thread.joinable()) { _thread.join(); } - _disconnect = false; + + _stop = false; + _state = State::CONNECTING; _thread = std::thread(&TwitchChatConnection::ConnectThread, this); } void TwitchChatConnection::Disconnect() { std::lock_guard lock(_connectMtx); - _disconnect = true; + if (_state == State::DISCONNECTED) { + vblog(LOG_INFO, "TwitchChatConnection already disconnected"); + return; + } + + _stop = true; + websocketpp::lib::error_code ec; _client.close(_connection, websocketpp::close::status::normal, "Twitch chat connection stopping", ec); + if (ec) { + blog(LOG_INFO, "TwitchChatConnection close failed: %s", + ec.message().c_str()); + } + { std::unique_lock waitLock(_waitMtx); _cv.notify_all(); } - while (_connected) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - _client.close(_connection, websocketpp::close::status::normal, - "Twitch chat connection stopping", ec); - } - if (_thread.joinable()) { _thread.join(); } - _connected = false; } static std::string toLowerCase(const std::string &str) @@ -468,7 +480,7 @@ static std::string toLowerCase(const std::string &str) void TwitchChatConnection::ConnectToChat() { - if (!_connected) { + if (_state == State::DISCONNECTED) { Connect(); return; } @@ -556,15 +568,24 @@ void TwitchChatConnection::HandleNotice(const IRCMessage &message) const void TwitchChatConnection::HandleReconnect() { blog(LOG_INFO, - "Received RECONNECT notice! Twitch chat connection will be terminated!"); - Disconnect(); + "Received RECONNECT notice! Twitch chat connection will be terminated shortly!"); + + // We need a separate thread here as we cannot close the current + // connection from within the message handler + std::thread reconnectThread([token = this->_token, + channel = this->_channel]() { + auto chatConnection = + TwitchChatConnection::GetChatConnection(token, channel); + chatConnection->Disconnect(); + chatConnection->Connect(); + }); + reconnectThread.detach(); } void TwitchChatConnection::OnOpen(connection_hdl) { vblog(LOG_INFO, "Twitch chat connection opened"); - _connected = true; - + _state = State::CONNECTED; Authenticate(); } @@ -620,7 +641,6 @@ void TwitchChatConnection::OnClose(connection_hdl hdl) con = _client.get_con_from_hdl(hdl); auto msg = con->get_ec().message(); blog(LOG_INFO, "Twitch chat connection closed: %s", msg.c_str()); - _connected = false; } void TwitchChatConnection::OnFail(connection_hdl hdl) @@ -629,7 +649,6 @@ void TwitchChatConnection::OnFail(connection_hdl hdl) con = _client.get_con_from_hdl(hdl); auto msg = con->get_ec().message(); blog(LOG_INFO, "Twitch chat connection failed: %s", msg.c_str()); - _connected = false; } void TwitchChatConnection::Send(const std::string &msg) diff --git a/plugins/twitch/chat-connection.hpp b/plugins/twitch/chat-connection.hpp index ed1757e3..fc786b4f 100644 --- a/plugins/twitch/chat-connection.hpp +++ b/plugins/twitch/chat-connection.hpp @@ -96,9 +96,12 @@ private: std::mutex _waitMtx; std::mutex _connectMtx; std::condition_variable _cv; - std::atomic_bool _connected{false}; + + enum class State { DISCONNECTED, CONNECTING, CONNECTED }; + std::atomic _state = {State::DISCONNECTED}; + std::atomic_bool _authenticated{false}; - std::atomic_bool _disconnect{false}; + std::atomic_bool _stop{false}; std::string _url; ChatMessageDispatcher _messageDispatcher;