This commit is contained in:
Mm2PL 2024-01-14 12:45:27 +00:00 committed by GitHub
commit f93fcd676b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 82 additions and 31 deletions

View file

@ -106,6 +106,7 @@
- Dev: Refactored the Image Uploader feature. (#4971) - Dev: Refactored the Image Uploader feature. (#4971)
- Dev: Refactored the SplitOverlay code. (#5082) - Dev: Refactored the SplitOverlay code. (#5082)
- Dev: Fixed deadlock and use-after-free in tests. (#4981) - Dev: Fixed deadlock and use-after-free in tests. (#4981)
- Dev: Cleanly exit Chatterino instead of force exiting. (#4993)
- Dev: Moved all `.clang-format` files to the root directory. (#5037) - Dev: Moved all `.clang-format` files to the root directory. (#5037)
- Dev: Load less message history upon reconnects. (#5001, #5018) - Dev: Load less message history upon reconnects. (#5001, #5018)
- Dev: Load less message history upon reconnects. (#5001) - Dev: Load less message history upon reconnects. (#5001)

View file

@ -114,6 +114,7 @@ Application::Application(Settings &_settings, Paths &_paths, const Args &_args)
, emotes(&this->emplace<Emotes>()) , emotes(&this->emplace<Emotes>())
, accounts(&this->emplace<AccountController>()) , accounts(&this->emplace<AccountController>())
, hotkeys(&this->emplace<HotkeyController>()) , hotkeys(&this->emplace<HotkeyController>())
, twitch(&this->emplace<TwitchIrcServer>())
, windows(&this->emplace<WindowManager>()) , windows(&this->emplace<WindowManager>())
, toasts(&this->emplace<Toasts>()) , toasts(&this->emplace<Toasts>())
, imageUploader(&this->emplace<ImageUploader>()) , imageUploader(&this->emplace<ImageUploader>())
@ -123,7 +124,6 @@ Application::Application(Settings &_settings, Paths &_paths, const Args &_args)
, commands(&this->emplace<CommandController>()) , commands(&this->emplace<CommandController>())
, notifications(&this->emplace<NotificationController>()) , notifications(&this->emplace<NotificationController>())
, highlights(&this->emplace<HighlightController>()) , highlights(&this->emplace<HighlightController>())
, twitch(&this->emplace<TwitchIrcServer>())
, chatterinoBadges(&this->emplace<ChatterinoBadges>()) , chatterinoBadges(&this->emplace<ChatterinoBadges>())
, ffzBadges(&this->emplace<FfzBadges>()) , ffzBadges(&this->emplace<FfzBadges>())
, seventvBadges(&this->emplace<SeventvBadges>()) , seventvBadges(&this->emplace<SeventvBadges>())
@ -147,11 +147,6 @@ Application::Application(Settings &_settings, Paths &_paths, const Args &_args)
Application::~Application() = default; Application::~Application() = default;
void Application::fakeDtor()
{
this->twitchPubSub.reset();
}
void Application::initialize(Settings &settings, Paths &paths) void Application::initialize(Settings &settings, Paths &paths)
{ {
assert(isAppInitialized == false); assert(isAppInitialized == false);

View file

@ -98,12 +98,6 @@ public:
Application &operator=(const Application &) = delete; Application &operator=(const Application &) = delete;
Application &operator=(Application &&) = delete; Application &operator=(Application &&) = delete;
/**
* In the interim, before we remove _exit(0); from RunGui.cpp,
* this will destroy things we know can be destroyed
*/
void fakeDtor();
void initialize(Settings &settings, Paths &paths); void initialize(Settings &settings, Paths &paths);
void load(); void load();
void save(); void save();
@ -117,6 +111,7 @@ public:
Emotes *const emotes{}; Emotes *const emotes{};
AccountController *const accounts{}; AccountController *const accounts{};
HotkeyController *const hotkeys{}; HotkeyController *const hotkeys{};
TwitchIrcServer *const twitch{};
WindowManager *const windows{}; WindowManager *const windows{};
Toasts *const toasts{}; Toasts *const toasts{};
ImageUploader *const imageUploader{}; ImageUploader *const imageUploader{};
@ -126,7 +121,6 @@ public:
CommandController *const commands{}; CommandController *const commands{};
NotificationController *const notifications{}; NotificationController *const notifications{};
HighlightController *const highlights{}; HighlightController *const highlights{};
TwitchIrcServer *const twitch{};
ChatterinoBadges *const chatterinoBadges{}; ChatterinoBadges *const chatterinoBadges{};
FfzBadges *const ffzBadges{}; FfzBadges *const ffzBadges{};
SeventvBadges *const seventvBadges{}; SeventvBadges *const seventvBadges{};

View file

@ -492,6 +492,7 @@ set(SOURCE_FILES
util/RapidjsonHelpers.hpp util/RapidjsonHelpers.hpp
util/RatelimitBucket.cpp util/RatelimitBucket.cpp
util/RatelimitBucket.hpp util/RatelimitBucket.hpp
util/RenameThread.hpp
util/SampleData.cpp util/SampleData.cpp
util/SampleData.hpp util/SampleData.hpp
util/SharedPtrElementLess.hpp util/SharedPtrElementLess.hpp

View file

@ -21,6 +21,7 @@
#include <QtConcurrent> #include <QtConcurrent>
#include <csignal> #include <csignal>
#include <thread>
#include <tuple> #include <tuple>
#ifdef USEWINSDK #ifdef USEWINSDK
@ -237,6 +238,7 @@ void runGui(QApplication &a, Paths &paths, Settings &settings, const Args &args)
#endif #endif
auto thread = std::thread([dir = paths.miscDirectory] { auto thread = std::thread([dir = paths.miscDirectory] {
#ifdef Q_OS_WIN32
{ {
auto path = combinePath(dir, "Update.exe"); auto path = combinePath(dir, "Update.exe");
if (QFile::exists(path)) if (QFile::exists(path))
@ -251,6 +253,7 @@ void runGui(QApplication &a, Paths &paths, Settings &settings, const Args &args)
QFile::remove(path); QFile::remove(path);
} }
} }
#endif
}); });
// Clear the cache 1 minute after start. // Clear the cache 1 minute after start.
@ -281,15 +284,16 @@ void runGui(QApplication &a, Paths &paths, Settings &settings, const Args &args)
pajlada::Settings::SettingManager::gSave(); pajlada::Settings::SettingManager::gSave();
} }
if (thread.joinable())
{
thread.join();
}
chatterino::NetworkManager::deinit(); chatterino::NetworkManager::deinit();
#ifdef USEWINSDK #ifdef USEWINSDK
// flushing windows clipboard to keep copied messages // flushing windows clipboard to keep copied messages
flushClipboard(); flushClipboard();
#endif #endif
app.fakeDtor();
_exit(0);
} }
} // namespace chatterino } // namespace chatterino

View file

@ -150,6 +150,15 @@ protected:
return this->started_.load(std::memory_order_acquire); return this->started_.load(std::memory_order_acquire);
} }
/**
* @brief Will be called when the clients has been requested to stop
*
* Derived classes can override this to do their own shutdown behaviour
*/
virtual void stopImpl()
{
}
liveupdates::WebsocketClient &websocketClient_; liveupdates::WebsocketClient &websocketClient_;
private: private:
@ -164,6 +173,8 @@ private:
{ {
assert(this->isStarted()); assert(this->isStarted());
this->started_.store(false, std::memory_order_release); this->started_.store(false, std::memory_order_release);
this->stopImpl();
} }
liveupdates::WebsocketHandle handle_; liveupdates::WebsocketHandle handle_;

View file

@ -8,6 +8,7 @@
#include "providers/twitch/PubSubHelpers.hpp" #include "providers/twitch/PubSubHelpers.hpp"
#include "util/DebugCount.hpp" #include "util/DebugCount.hpp"
#include "util/ExponentialBackoff.hpp" #include "util/ExponentialBackoff.hpp"
#include "util/RenameThread.hpp"
#include <pajlada/signals/signal.hpp> #include <pajlada/signals/signal.hpp>
#include <QJsonObject> #include <QJsonObject>
@ -94,7 +95,10 @@ public:
.toStdString()); .toStdString());
} }
virtual ~BasicPubSubManager() = default; virtual ~BasicPubSubManager()
{
this->stop();
};
BasicPubSubManager(const BasicPubSubManager &) = delete; BasicPubSubManager(const BasicPubSubManager &) = delete;
BasicPubSubManager(const BasicPubSubManager &&) = delete; BasicPubSubManager(const BasicPubSubManager &&) = delete;
@ -115,6 +119,8 @@ public:
this->mainThread_.reset(new std::thread([this] { this->mainThread_.reset(new std::thread([this] {
runThread(); runThread();
})); }));
renameThread(*this->mainThread_.get(), "BPSM");
} }
void stop() void stop()

View file

@ -13,6 +13,8 @@ Client::Client(liveupdates::WebsocketClient &websocketClient,
: BasicPubSubClient<Subscription>(websocketClient, std::move(handle)) : BasicPubSubClient<Subscription>(websocketClient, std::move(handle))
, lastHeartbeat_(std::chrono::steady_clock::now()) , lastHeartbeat_(std::chrono::steady_clock::now())
, heartbeatInterval_(heartbeatInterval) , heartbeatInterval_(heartbeatInterval)
, heartbeatTimer_(std::make_shared<boost::asio::steady_timer>(
this->websocketClient_.get_io_service()))
{ {
} }
@ -23,6 +25,11 @@ void Client::onConnectionEstablished()
this->checkHeartbeat(); this->checkHeartbeat();
} }
void Client::stopImpl()
{
this->heartbeatTimer_->cancel();
}
void Client::setHeartbeatInterval(int intervalMs) void Client::setHeartbeatInterval(int intervalMs)
{ {
qCDebug(chatterinoSeventvEventAPI) qCDebug(chatterinoSeventvEventAPI)
@ -54,8 +61,7 @@ void Client::checkHeartbeat()
auto self = std::dynamic_pointer_cast<Client>(this->shared_from_this()); auto self = std::dynamic_pointer_cast<Client>(this->shared_from_this());
runAfter(this->websocketClient_.get_io_service(), this->heartbeatInterval_, runAfter(this->heartbeatTimer_, this->heartbeatInterval_, [self](auto) {
[self](auto) {
if (!self->isStarted()) if (!self->isStarted())
{ {
return; return;

View file

@ -24,6 +24,7 @@ public:
protected: protected:
void onConnectionEstablished() override; void onConnectionEstablished() override;
void stopImpl() override;
private: private:
void checkHeartbeat(); void checkHeartbeat();
@ -32,6 +33,7 @@ private:
lastHeartbeat_; lastHeartbeat_;
// This will be set once on the welcome message. // This will be set once on the welcome message.
std::chrono::milliseconds heartbeatInterval_; std::chrono::milliseconds heartbeatInterval_;
std::shared_ptr<boost::asio::steady_timer> heartbeatTimer_;
friend SeventvEventAPI; friend SeventvEventAPI;
}; };

View file

@ -125,7 +125,16 @@ WindowManager::WindowManager()
}); });
} }
WindowManager::~WindowManager() = default; WindowManager::~WindowManager()
{
for (const auto &window : this->windows_)
{
// We would prefer to use window->deleteLater() here, but the timings are too tight
// Channel's completion model gets destroyed before the deleteLater call actually deletes the
// UI objects that rely on the completion model
delete window;
}
}
MessageElementFlags WindowManager::getWordFlags() MessageElementFlags WindowManager::getWordFlags()
{ {

19
src/util/RenameThread.hpp Normal file
View file

@ -0,0 +1,19 @@
#pragma once
#include <QtGlobal>
#ifdef Q_OS_LINUX
# include <pthread.h>
#endif
namespace chatterino {
template <typename T>
void renameThread(T &&thread, const char *threadName)
{
#ifdef Q_OS_LINUX
pthread_setname_np(thread.native_handle(), threadName);
#endif
}
} // namespace chatterino

View file

@ -50,13 +50,13 @@ SplitInput::SplitInput(QWidget *parent, Split *_chatWidget,
this->installEventFilter(this); this->installEventFilter(this);
this->initLayout(); this->initLayout();
auto completer = auto *completer =
new QCompleter(&this->split_->getChannel()->completionModel); new QCompleter(&this->split_->getChannel()->completionModel, this);
this->ui_.textEdit->setCompleter(completer); this->ui_.textEdit->setCompleter(completer);
this->signalHolder_.managedConnect(this->split_->channelChanged, [this] { this->signalHolder_.managedConnect(this->split_->channelChanged, [this] {
auto channel = this->split_->getChannel(); auto channel = this->split_->getChannel();
auto completer = new QCompleter(&channel->completionModel); auto *completer = new QCompleter(&channel->completionModel, this);
this->ui_.textEdit->setCompleter(completer); this->ui_.textEdit->setCompleter(completer);
}); });
@ -76,6 +76,8 @@ SplitInput::SplitInput(QWidget *parent, Split *_chatWidget,
}); });
} }
SplitInput::~SplitInput() = default;
void SplitInput::initLayout() void SplitInput::initLayout()
{ {
auto app = getApp(); auto app = getApp();

View file

@ -24,7 +24,7 @@ class ResizingTextEdit;
class ChannelView; class ChannelView;
enum class CompletionKind; enum class CompletionKind;
class SplitInput : public BaseWidget class SplitInput final : public BaseWidget
{ {
Q_OBJECT Q_OBJECT
@ -32,6 +32,7 @@ public:
SplitInput(Split *_chatWidget, bool enableInlineReplying = true); SplitInput(Split *_chatWidget, bool enableInlineReplying = true);
SplitInput(QWidget *parent, Split *_chatWidget, ChannelView *_channelView, SplitInput(QWidget *parent, Split *_chatWidget, ChannelView *_channelView,
bool enableInlineReplying = true); bool enableInlineReplying = true);
~SplitInput() override;
bool hasSelection() const; bool hasSelection() const;
void clearSelection() const; void clearSelection() const;