Fix notification flood

Avoid sending status changes in between queued operations
This commit is contained in:
woblight
2024-08-16 10:50:59 +02:00
parent fff94a9279
commit 24a96f4fc9
6 changed files with 68 additions and 21 deletions
+2 -2
View File
@@ -36,10 +36,10 @@ Pane {
hoverEnabled: true hoverEnabled: true
ToolTip.visible: hovered ToolTip.visible: hovered
ToolTip.text: qsTr("Path to the folder containing the addons") ToolTip.text: qsTr("Path to the folder containing the addons")
enabled: Control.status == Control.Ready && addonsReady enabled: Engine.status == Engine.Ready && addonsReady
} }
Button { Button {
enabled: Control.status == Control.Ready && addonsReady enabled: Engine.status == Engine.Ready && addonsReady
text: qsTr("Browse") text: qsTr("Browse")
onClicked: {fileDialog.selector = index; fileDialog.currentFolder = "file://" + Engine.addonsPaths[index]; fileDialog.visible = true} onClicked: {fileDialog.selector = index; fileDialog.currentFolder = "file://" + Engine.addonsPaths[index]; fileDialog.visible = true}
hoverEnabled: true hoverEnabled: true
+6 -3
View File
@@ -691,7 +691,7 @@ AutoPtr<git_reference> Addon::branchRef(QString name)
void Addon::delegate(QString taskname, auto work, auto callback, bool force) void Addon::delegate(QString taskname, auto work, auto callback, bool force)
{ {
Q_ASSERT_X(QThread::currentThread() == thread(), "delegate", "Attempt to delegate from another thread."); Q_ASSERT_X(QThread::currentThread() == thread(), "delegate", "Attempt to delegate from another thread.");
if (status() != Status::Ready && !force) { if (m_lock && !force) {
qInfo() << name() << "Enqueueing" << taskname; qInfo() << name() << "Enqueueing" << taskname;
m_tasks.enqueue({taskname, [this, taskname, work, callback](){delegate(taskname, work,callback);}}); m_tasks.enqueue({taskname, [this, taskname, work, callback](){delegate(taskname, work,callback);}});
return; return;
@@ -700,10 +700,12 @@ void Addon::delegate(QString taskname, auto work, auto callback, bool force)
setProgress(0); setProgress(0);
setTotal(0); setTotal(0);
setStatus(Status::Busy); setStatus(Status::Busy);
m_lock = true;
using ret_t = typename std::invoke_result<decltype(work)>::type; using ret_t = typename std::invoke_result<decltype(work)>::type;
QFuture<ret_t> fut = QtConcurrent::run(m_pool, work); QFuture<ret_t> fut = QtConcurrent::run(m_pool, work);
QFutureWatcher<ret_t> *fw = new QFutureWatcher<ret_t>(); QFutureWatcher<ret_t> *fw = new QFutureWatcher<ret_t>();
connect(fw, &QFutureWatcher<ret_t>::finished, [this, callback, fw, taskname](){ connect(fw, &QFutureWatcher<ret_t>::finished, [this, callback, fw, taskname](){
m_lock = false;
if (fw->isCanceled()) { if (fw->isCanceled()) {
try { try {
fw->waitForFinished(); fw->waitForFinished();
@@ -714,7 +716,6 @@ void Addon::delegate(QString taskname, auto work, auto callback, bool force)
} }
return; return;
} }
setStatus(Status::Ready);
TaskQueue old; TaskQueue old;
m_tasks.swap(old); m_tasks.swap(old);
@@ -725,7 +726,9 @@ void Addon::delegate(QString taskname, auto work, auto callback, bool force)
m_tasks.append(old); m_tasks.append(old);
if (!m_tasks.isEmpty() && status() != Status::Busy) if (m_tasks.empty())
setStatus(Status::Ready);
else if (!m_lock)
m_tasks.dequeue().second(); m_tasks.dequeue().second();
}); });
connect(fw, &QFutureWatcher<ret_t>::finished, fw, &QFutureWatcher<ret_t>::deleteLater); connect(fw, &QFutureWatcher<ret_t>::finished, fw, &QFutureWatcher<ret_t>::deleteLater);
+2
View File
@@ -59,6 +59,8 @@ class Addon : public QObject
QMutex m_mutex; QMutex m_mutex;
QWaitCondition m_wait; QWaitCondition m_wait;
void *m_result; void *m_result;
bool m_lock = false;
public: public:
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QStringList branches READ branches WRITE setBranches NOTIFY branchesChanged) Q_PROPERTY(QStringList branches READ branches WRITE setBranches NOTIFY branchesChanged)
+31 -5
View File
@@ -86,7 +86,7 @@ void Control::removeTocSuffixes(QString &string)
void Control::delegate(QString taskname, auto work, auto callback) void Control::delegate(QString taskname, auto work, auto callback)
{ {
Q_ASSERT_X(QThread::currentThread() == thread(), "delegate", "Attempt to delegate from another thread."); Q_ASSERT_X(QThread::currentThread() == thread(), "delegate", "Attempt to delegate from another thread.");
if (status() != Status::Ready) { if (m_lock) {
qInfo() << "Enqueueing" << taskname; qInfo() << "Enqueueing" << taskname;
m_tasks.enqueue({taskname, [this, taskname, work, callback](){delegate(taskname, work,callback);}}); m_tasks.enqueue({taskname, [this, taskname, work, callback](){delegate(taskname, work,callback);}});
return; return;
@@ -95,12 +95,13 @@ void Control::delegate(QString taskname, auto work, auto callback)
setStatusMessage(taskname); setStatusMessage(taskname);
setProgress(0); setProgress(0);
setTotal(0); setTotal(0);
setStatus(Status::Busy); BusyLock lock(this);
m_lock = true;
using ret_t = typename std::invoke_result<decltype(work)>::type; using ret_t = typename std::invoke_result<decltype(work)>::type;
QFuture<ret_t> fut = QtConcurrent::run(m_pool, work); QFuture<ret_t> fut = QtConcurrent::run(m_pool, work);
QFutureWatcher<ret_t> *fw = new QFutureWatcher<ret_t>(); QFutureWatcher<ret_t> *fw = new QFutureWatcher<ret_t>();
connect(fw, &QFutureWatcher<ret_t>::finished, [this, callback, fw](){ connect(fw, &QFutureWatcher<ret_t>::finished, [this, callback, fw, lock=std::move(lock)](){
setStatus(Status::Ready); m_lock = false;
TaskQueue old; TaskQueue old;
m_tasks.swap(old); m_tasks.swap(old);
@@ -111,7 +112,9 @@ void Control::delegate(QString taskname, auto work, auto callback)
m_tasks.append(old); m_tasks.append(old);
if (!m_tasks.isEmpty() && status() != Status::Busy) if (m_tasks.empty())
setStatus(Status::Ready);
else if (!m_lock)
m_tasks.dequeue().second(); m_tasks.dequeue().second();
}); });
connect(fw, &QFutureWatcher<ret_t>::finished, fw, &QFutureWatcher<ret_t>::deleteLater); connect(fw, &QFutureWatcher<ret_t>::finished, fw, &QFutureWatcher<ret_t>::deleteLater);
@@ -340,6 +343,7 @@ void Control::saveAddonsPaths()
void Control::scanForAddons(int i) void Control::scanForAddons(int i)
{ {
if (i == -1) { if (i == -1) {
BusyLock lock(this);
foreach (Addon *addon, m_addons) foreach (Addon *addon, m_addons)
addon->deleteLater(); addon->deleteLater();
QList<Addon *> ol; QList<Addon *> ol;
@@ -703,3 +707,25 @@ bool Control::selfUpdates() const
return false; return false;
#endif #endif
} }
int Control::BusyLock::count = 0;
Control::BusyLock::BusyLock(Control *c)
{
m_locked = c;
m_locked->setStatus(Status::Busy);
count++;
}
Control::BusyLock::BusyLock(BusyLock &&other)
{
m_locked = other.m_locked;
other.m_locked = nullptr;
}
Control::BusyLock::~BusyLock()
{
if (m_locked)
if (!--count && m_locked->m_tasks.empty())
m_locked->setStatus(Status::Ready);
}
+12
View File
@@ -45,14 +45,26 @@ public:
int check_git_return(int code); int check_git_return(int code);
class Control : public QObject class Control : public QObject
{ {
class BusyLock
{
static int count;
Control* m_locked = nullptr;
public:
BusyLock(Control* c);
BusyLock(BusyLock&& other);
~BusyLock();
};
Q_OBJECT Q_OBJECT
using TaskQueue = QQueue<QPair<QString,std::function<void()>>>; using TaskQueue = QQueue<QPair<QString,std::function<void()>>>;
TaskQueue m_tasks; TaskQueue m_tasks;
QList<Addon *> m_addons; QList<Addon *> m_addons;
bool m_lock = false;
public: public:
Q_PROPERTY(QString version READ version CONSTANT) Q_PROPERTY(QString version READ version CONSTANT)
+15 -11
View File
@@ -30,7 +30,7 @@ ApplicationWindow {
property bool addonsReady: false property bool addonsReady: false
Component.onCompleted: { Component.onCompleted: {
availableUpdates = Qt.binding(function(){ availableUpdates = Qt.binding(function(){
if (!addonsReady) if (!(Engine.status == Engine.Ready && addonsReady))
return -1; return -1;
var count = 0 var count = 0
for (var i = 0; i < Engine.addons.length; i++) { for (var i = 0; i < Engine.addons.length; i++) {
@@ -40,16 +40,18 @@ ApplicationWindow {
} }
return count return count
}) })
addonsReady = Qt.binding(function(){ addonsReady = Qt.binding(checkAddonsReady)
for (var i = 0; i < Engine.addons.length; i++) {
var addon = Engine.addons[i]
if (addon.status == Addon.Status.Busy)
return false
}
return true
})
} }
function checkAddonsReady(){
for (var i = 0; i < Engine.addons.length; i++) {
var addon = Engine.addons[i]
console.debug(addon.status)
if (addon.status == Addon.Status.Busy)
return false
}
return true
}
function updateAll() { function updateAll() {
for (var i = 0; i < Engine.addons.length; i++) { for (var i = 0; i < Engine.addons.length; i++) {
var addon = Engine.addons[i] var addon = Engine.addons[i]
@@ -59,6 +61,8 @@ ApplicationWindow {
} }
onAvailableUpdatesChanged: { onAvailableUpdatesChanged: {
if (availableUpdates == -1)
return;
if (availableUpdates > 0) { if (availableUpdates > 0) {
tray.showMessage("Addons updates available", "You have " + availableUpdates + " addons waiting for update.", 1, 5000) tray.showMessage("Addons updates available", "You have " + availableUpdates + " addons waiting for update.", 1, 5000)
} else { } else {
@@ -81,7 +85,7 @@ ApplicationWindow {
icon.name: "view-refresh" icon.name: "view-refresh"
onTriggered: Engine.scanForAddons() onTriggered: Engine.scanForAddons()
shortcut: StandardKey.Refresh shortcut: StandardKey.Refresh
enabled: Control.status == Control.Ready && addonsReady enabled: Engine.status == Engine.Ready && addonsReady
} }
Action { Action {
id: addAction id: addAction
@@ -94,7 +98,7 @@ ApplicationWindow {
icon.name: "export-symbolic" icon.name: "export-symbolic"
onTriggered: exportListDialog.visible = true onTriggered: exportListDialog.visible = true
enabled: Control.status == Control.Ready && addonsReady enabled: Engine.status == Engine.Ready && addonsReady
shortcut: StandardKey.SaveAs shortcut: StandardKey.SaveAs
} }
Action { Action {