mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
lock SignalVector with shared_lock to allow reading on other threads
This commit is contained in:
parent
4e4c7d4c0b
commit
fff979b3c0
12 changed files with 120 additions and 35 deletions
|
@ -163,7 +163,7 @@ void CompletionModel::refresh(const QString &prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
for (auto &command : getApp()->commands->items_.getVector())
|
for (auto &command : getApp()->commands->items_)
|
||||||
{
|
{
|
||||||
addString(command.name, TaggedString::Command);
|
addString(command.name, TaggedString::Command);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <pajlada/signals/signal.hpp>
|
#include <pajlada/signals/signal.hpp>
|
||||||
|
#include <shared_mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "debug/AssertInGuiThread.hpp"
|
#include "debug/AssertInGuiThread.hpp"
|
||||||
|
@ -20,7 +21,57 @@ struct SignalVectorItemArgs {
|
||||||
template <typename TVectorItem>
|
template <typename TVectorItem>
|
||||||
class ReadOnlySignalVector : boost::noncopyable
|
class ReadOnlySignalVector : boost::noncopyable
|
||||||
{
|
{
|
||||||
|
using VecIt = typename std::vector<TVectorItem>::iterator;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
struct Iterator
|
||||||
|
: public std::iterator<std::input_iterator_tag, TVectorItem> {
|
||||||
|
Iterator(VecIt &&it, std::shared_mutex &mutex)
|
||||||
|
: it_(std::move(it))
|
||||||
|
, lock_(mutex)
|
||||||
|
, mutex_(mutex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator(const Iterator &other)
|
||||||
|
: it_(other.it_)
|
||||||
|
, lock_(other.mutex_)
|
||||||
|
, mutex_(other.mutex_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TVectorItem &operator*()
|
||||||
|
{
|
||||||
|
return it_.operator*();
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator &operator++()
|
||||||
|
{
|
||||||
|
++this->it_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Iterator &other)
|
||||||
|
{
|
||||||
|
return this->it_ == other.it_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Iterator &other)
|
||||||
|
{
|
||||||
|
return this->it_ != other.it_;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto operator-(const Iterator &other)
|
||||||
|
{
|
||||||
|
return this->it_ - other.it_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
VecIt it_;
|
||||||
|
std::shared_lock<std::shared_mutex> lock_;
|
||||||
|
std::shared_mutex &mutex_;
|
||||||
|
};
|
||||||
|
|
||||||
ReadOnlySignalVector()
|
ReadOnlySignalVector()
|
||||||
{
|
{
|
||||||
QObject::connect(&this->itemsChangedTimer_, &QTimer::timeout,
|
QObject::connect(&this->itemsChangedTimer_, &QTimer::timeout,
|
||||||
|
@ -34,6 +85,27 @@ public:
|
||||||
pajlada::Signals::Signal<SignalVectorItemArgs<TVectorItem>> itemRemoved;
|
pajlada::Signals::Signal<SignalVectorItemArgs<TVectorItem>> itemRemoved;
|
||||||
pajlada::Signals::NoArgSignal delayedItemsChanged;
|
pajlada::Signals::NoArgSignal delayedItemsChanged;
|
||||||
|
|
||||||
|
Iterator begin() const
|
||||||
|
{
|
||||||
|
return Iterator(
|
||||||
|
const_cast<std::vector<TVectorItem> &>(this->vector_).begin(),
|
||||||
|
this->mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator end() const
|
||||||
|
{
|
||||||
|
return Iterator(
|
||||||
|
const_cast<std::vector<TVectorItem> &>(this->vector_).end(),
|
||||||
|
this->mutex_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
std::shared_lock lock(this->mutex_);
|
||||||
|
|
||||||
|
return this->vector_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<TVectorItem> &getVector() const
|
const std::vector<TVectorItem> &getVector() const
|
||||||
{
|
{
|
||||||
assertInGuiThread();
|
assertInGuiThread();
|
||||||
|
@ -56,6 +128,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
std::vector<TVectorItem> vector_;
|
std::vector<TVectorItem> vector_;
|
||||||
QTimer itemsChangedTimer_;
|
QTimer itemsChangedTimer_;
|
||||||
|
mutable std::shared_mutex mutex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename TVectorItem>
|
template <typename TVectorItem>
|
||||||
|
@ -69,10 +142,15 @@ public:
|
||||||
void removeItem(int index, void *caller = nullptr)
|
void removeItem(int index, void *caller = nullptr)
|
||||||
{
|
{
|
||||||
assertInGuiThread();
|
assertInGuiThread();
|
||||||
assert(index >= 0 && index < this->vector_.size());
|
std::unique_lock lock(this->mutex_);
|
||||||
|
|
||||||
|
assert(index >= 0 && index < int(this->vector_.size()));
|
||||||
|
|
||||||
TVectorItem item = this->vector_[index];
|
TVectorItem item = this->vector_[index];
|
||||||
|
|
||||||
this->vector_.erase(this->vector_.begin() + index);
|
this->vector_.erase(this->vector_.begin() + index);
|
||||||
|
lock.unlock(); // manual unlock
|
||||||
|
|
||||||
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
|
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
|
||||||
this->itemRemoved.invoke(args);
|
this->itemRemoved.invoke(args);
|
||||||
|
|
||||||
|
@ -93,16 +171,20 @@ public:
|
||||||
void *caller = nullptr) override
|
void *caller = nullptr) override
|
||||||
{
|
{
|
||||||
assertInGuiThread();
|
assertInGuiThread();
|
||||||
if (index == -1)
|
|
||||||
{
|
|
||||||
index = this->vector_.size();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(index >= 0 && index <= this->vector_.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
this->vector_.insert(this->vector_.begin() + index, item);
|
{
|
||||||
|
std::unique_lock lock(this->mutex_);
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
index = this->vector_.size();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(index >= 0 && index <= this->vector_.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
this->vector_.insert(this->vector_.begin() + index, item);
|
||||||
|
}
|
||||||
|
|
||||||
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
|
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
|
||||||
this->itemInserted.invoke(args);
|
this->itemInserted.invoke(args);
|
||||||
|
@ -124,11 +206,16 @@ public:
|
||||||
void *caller = nullptr) override
|
void *caller = nullptr) override
|
||||||
{
|
{
|
||||||
assertInGuiThread();
|
assertInGuiThread();
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
auto it = std::lower_bound(this->vector_.begin(), this->vector_.end(),
|
{
|
||||||
item, Compare{});
|
std::unique_lock lock(this->mutex_);
|
||||||
int index = it - this->vector_.begin();
|
|
||||||
this->vector_.insert(it, item);
|
auto it = std::lower_bound(this->vector_.begin(),
|
||||||
|
this->vector_.end(), item, Compare{});
|
||||||
|
index = it - this->vector_.begin();
|
||||||
|
this->vector_.insert(it, item);
|
||||||
|
}
|
||||||
|
|
||||||
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
|
SignalVectorItemArgs<TVectorItem> args{item, index, caller};
|
||||||
this->itemInserted.invoke(args);
|
this->itemInserted.invoke(args);
|
||||||
|
|
|
@ -16,7 +16,7 @@ AccountController::AccountController()
|
||||||
this->twitch.accounts.itemRemoved.connect([this](const auto &args) {
|
this->twitch.accounts.itemRemoved.connect([this](const auto &args) {
|
||||||
if (args.caller != this)
|
if (args.caller != this)
|
||||||
{
|
{
|
||||||
auto &accs = this->twitch.accounts.getVector();
|
auto &accs = this->twitch.accounts;
|
||||||
auto it = std::find(accs.begin(), accs.end(), args.item);
|
auto it = std::find(accs.begin(), accs.end(), args.item);
|
||||||
assert(it != accs.end());
|
assert(it != accs.end());
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ AccountController::AccountController()
|
||||||
{
|
{
|
||||||
case ProviderId::Twitch:
|
case ProviderId::Twitch:
|
||||||
{
|
{
|
||||||
auto &accs = this->twitch.accounts.getVector();
|
auto &accs = this->twitch.accounts;
|
||||||
auto it = std::find(accs.begin(), accs.end(), args.item);
|
auto it = std::find(accs.begin(), accs.end(), args.item);
|
||||||
assert(it != accs.end());
|
assert(it != accs.end());
|
||||||
|
|
||||||
|
|
|
@ -174,7 +174,7 @@ void CommandController::initialize(Settings &, Paths &paths)
|
||||||
auto addFirstMatchToMap = [this](auto args) {
|
auto addFirstMatchToMap = [this](auto args) {
|
||||||
this->commandsMap_.remove(args.item.name);
|
this->commandsMap_.remove(args.item.name);
|
||||||
|
|
||||||
for (const Command &cmd : this->items_.getVector())
|
for (const Command &cmd : this->items_)
|
||||||
{
|
{
|
||||||
if (cmd.name == args.item.name)
|
if (cmd.name == args.item.name)
|
||||||
{
|
{
|
||||||
|
@ -185,7 +185,7 @@ void CommandController::initialize(Settings &, Paths &paths)
|
||||||
|
|
||||||
int maxSpaces = 0;
|
int maxSpaces = 0;
|
||||||
|
|
||||||
for (const Command &cmd : this->items_.getVector())
|
for (const Command &cmd : this->items_)
|
||||||
{
|
{
|
||||||
auto localMaxSpaces = cmd.name.count(' ');
|
auto localMaxSpaces = cmd.name.count(' ');
|
||||||
if (localMaxSpaces > maxSpaces)
|
if (localMaxSpaces > maxSpaces)
|
||||||
|
|
|
@ -64,7 +64,7 @@ UserHighlightModel *HighlightController::createUserModel(QObject *parent)
|
||||||
|
|
||||||
bool HighlightController::isHighlightedUser(const QString &username)
|
bool HighlightController::isHighlightedUser(const QString &username)
|
||||||
{
|
{
|
||||||
const auto &userItems = this->highlightedUsers.getVector();
|
const auto &userItems = this->highlightedUsers;
|
||||||
for (const auto &highlightedUser : userItems)
|
for (const auto &highlightedUser : userItems)
|
||||||
{
|
{
|
||||||
if (highlightedUser.isMatch(username))
|
if (highlightedUser.isMatch(username))
|
||||||
|
@ -87,9 +87,7 @@ HighlightBlacklistModel *HighlightController::createBlacklistModel(
|
||||||
|
|
||||||
bool HighlightController::blacklistContains(const QString &username)
|
bool HighlightController::blacklistContains(const QString &username)
|
||||||
{
|
{
|
||||||
std::vector<HighlightBlacklistUser> blacklistItems =
|
for (const auto &blacklistedUser : this->blacklistedUsers)
|
||||||
this->blacklistedUsers.getVector();
|
|
||||||
for (const auto &blacklistedUser : blacklistItems)
|
|
||||||
{
|
{
|
||||||
if (blacklistedUser.isMatch(username))
|
if (blacklistedUser.isMatch(username))
|
||||||
{
|
{
|
||||||
|
|
|
@ -107,7 +107,7 @@ public:
|
||||||
if (!this->emotesChecked_)
|
if (!this->emotesChecked_)
|
||||||
{
|
{
|
||||||
const auto &accvec =
|
const auto &accvec =
|
||||||
getApp()->accounts->twitch.accounts.getVector();
|
getApp()->accounts->twitch.accounts;
|
||||||
for (const auto &acc : accvec)
|
for (const auto &acc : accvec)
|
||||||
{
|
{
|
||||||
const auto &accemotes = *acc->accessEmotes();
|
const auto &accemotes = *acc->accessEmotes();
|
||||||
|
|
|
@ -41,7 +41,7 @@ void NotificationController::initialize(Settings &settings, Paths &paths)
|
||||||
|
|
||||||
this->channelMap[Platform::Mixer].delayedItemsChanged.connect([this] { //
|
this->channelMap[Platform::Mixer].delayedItemsChanged.connect([this] { //
|
||||||
this->mixerSetting_.setValue(
|
this->mixerSetting_.setValue(
|
||||||
this->channelMap[Platform::Mixer].getVector());
|
this->channelMap[Platform::Mixer]);
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
liveStatusTimer_ = new QTimer();
|
liveStatusTimer_ = new QTimer();
|
||||||
|
@ -69,7 +69,7 @@ void NotificationController::updateChannelNotification(
|
||||||
bool NotificationController::isChannelNotified(const QString &channelName,
|
bool NotificationController::isChannelNotified(const QString &channelName,
|
||||||
Platform p)
|
Platform p)
|
||||||
{
|
{
|
||||||
for (const auto &channel : this->channelMap[p].getVector())
|
for (const auto &channel : this->channelMap[p])
|
||||||
{
|
{
|
||||||
if (channelName.toLower() == channel.toLower())
|
if (channelName.toLower() == channel.toLower())
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,7 +25,7 @@ PingModel *PingController::createModel(QObject *parent)
|
||||||
|
|
||||||
bool PingController::isMuted(const QString &channelName)
|
bool PingController::isMuted(const QString &channelName)
|
||||||
{
|
{
|
||||||
for (const auto &channel : this->channelVector.getVector())
|
for (const auto &channel : this->channelVector)
|
||||||
{
|
{
|
||||||
if (channelName.toLower() == channel.toLower())
|
if (channelName.toLower() == channel.toLower())
|
||||||
{
|
{
|
||||||
|
|
|
@ -348,7 +348,7 @@ void TwitchModerationElement::addToContainer(MessageLayoutContainer &container,
|
||||||
int(container.getScale() * 16));
|
int(container.getScale() * 16));
|
||||||
|
|
||||||
for (const auto &action :
|
for (const auto &action :
|
||||||
getApp()->moderationActions->items.getVector())
|
getApp()->moderationActions->items)
|
||||||
{
|
{
|
||||||
if (auto image = action.getImage())
|
if (auto image = action.getImage())
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,7 @@ std::vector<QString> TwitchAccountManager::getUsernames() const
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(this->mutex_);
|
std::lock_guard<std::mutex> lock(this->mutex_);
|
||||||
|
|
||||||
for (const auto &user : this->accounts.getVector())
|
for (const auto &user : this->accounts)
|
||||||
{
|
{
|
||||||
userNames.push_back(user->getUserName());
|
userNames.push_back(user->getUserName());
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ std::shared_ptr<TwitchAccount> TwitchAccountManager::findUserByUsername(
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(this->mutex_);
|
std::lock_guard<std::mutex> lock(this->mutex_);
|
||||||
|
|
||||||
for (const auto &user : this->accounts.getVector())
|
for (const auto &user : this->accounts)
|
||||||
{
|
{
|
||||||
if (username.compare(user->getUserName(), Qt::CaseInsensitive) == 0)
|
if (username.compare(user->getUserName(), Qt::CaseInsensitive) == 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -60,7 +60,7 @@ bool TwitchMessageBuilder::isIgnored() const
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
|
|
||||||
// TODO(pajlada): Do we need to check if the phrase is valid first?
|
// TODO(pajlada): Do we need to check if the phrase is valid first?
|
||||||
for (const auto &phrase : app->ignores->phrases.getVector())
|
for (const auto &phrase : app->ignores->phrases)
|
||||||
{
|
{
|
||||||
if (phrase.isBlock() && phrase.isMatch(this->originalMessage_))
|
if (phrase.isBlock() && phrase.isMatch(this->originalMessage_))
|
||||||
{
|
{
|
||||||
|
@ -221,7 +221,7 @@ MessagePtr TwitchMessageBuilder::build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto app = getApp();
|
auto app = getApp();
|
||||||
const auto &phrases = app->ignores->phrases.getVector();
|
const auto &phrases = app->ignores->phrases;
|
||||||
auto removeEmotesInRange =
|
auto removeEmotesInRange =
|
||||||
[](int pos, int len,
|
[](int pos, int len,
|
||||||
std::vector<std::tuple<int, EmotePtr, EmoteName>>
|
std::vector<std::tuple<int, EmotePtr, EmoteName>>
|
||||||
|
|
|
@ -177,7 +177,7 @@ void SplitHeader::initializeLayout()
|
||||||
{
|
{
|
||||||
case Qt::LeftButton:
|
case Qt::LeftButton:
|
||||||
if (getApp()
|
if (getApp()
|
||||||
->moderationActions->items.getVector()
|
->moderationActions->items
|
||||||
.empty())
|
.empty())
|
||||||
{
|
{
|
||||||
getApp()->windows->showSettingsDialog(
|
getApp()->windows->showSettingsDialog(
|
||||||
|
@ -224,7 +224,7 @@ void SplitHeader::initializeLayout()
|
||||||
// update moderation button when items changed
|
// update moderation button when items changed
|
||||||
this->managedConnect(
|
this->managedConnect(
|
||||||
getApp()->moderationActions->items.delayedItemsChanged, [this] {
|
getApp()->moderationActions->items.delayedItemsChanged, [this] {
|
||||||
if (getApp()->moderationActions->items.getVector().empty())
|
if (getApp()->moderationActions->items.empty())
|
||||||
{
|
{
|
||||||
if (this->split_->getModerationMode())
|
if (this->split_->getModerationMode())
|
||||||
this->split_->setModerationMode(true);
|
this->split_->setModerationMode(true);
|
||||||
|
@ -509,7 +509,7 @@ void SplitHeader::updateModerationModeIcon()
|
||||||
{
|
{
|
||||||
auto moderationMode =
|
auto moderationMode =
|
||||||
this->split_->getModerationMode() &&
|
this->split_->getModerationMode() &&
|
||||||
!getApp()->moderationActions->items.getVector().empty();
|
!getApp()->moderationActions->items.empty();
|
||||||
|
|
||||||
this->moderationButton_->setPixmap(
|
this->moderationButton_->setPixmap(
|
||||||
moderationMode ? getApp()->resources->buttons.modModeEnabled
|
moderationMode ? getApp()->resources->buttons.modModeEnabled
|
||||||
|
|
Loading…
Reference in a new issue