[qrvthtool] ConfigStore::notifyAll(): Fix a deadlock if the config file has an invalid language tag.

The language tag is initially loaded by ConfigStore, but when notifyAll()
is called, LanguageMenu realizes it's invalid and calls ConfigStore::set()
with an empty tag, which causes a deadlock due to d->mtxSignalMaps being
locked.

To fix this, populate a single vector of methods to invoke, then unlock
the mutex and invoke the methods.
This commit is contained in:
David Korth 2025-05-26 14:51:27 -04:00
parent 8d8925ed0b
commit fd4a4ad0a9

View File

@ -665,9 +665,24 @@ void ConfigStore::unregisterChangeNotification(const QString &property, QObject
void ConfigStore::notifyAll(void)
{
// Invoke methods for registered objects.
// NOTE: To prevent deadlocks, we'll need to build up a list of
// objects and methods to invoke, then invoke them.
Q_D(ConfigStore);
QMutexLocker mtxLocker(&d->mtxSignalMaps);
struct ToInvoke_t {
QObject *object;
int method_idx;
QVariant value;
ToInvoke_t(QObject *object, int method_idx, const QVariant &value)
: object(object)
, method_idx(method_idx)
, value(value)
{ }
};
std::vector<ToInvoke_t> toInvoke;
for (auto &kv : d->signalMaps) {
const QString &property = kv.first;
vector<ConfigStorePrivate::SignalMap> &signalMapVector = kv.second;
@ -689,8 +704,14 @@ void ConfigStore::notifyAll(void)
signalMapVector.erase(signalMapVector.begin() + i);
} else {
// Invoke this method.
ConfigStorePrivate::InvokeQtMethod(smap.object, smap.method_idx, value);
toInvoke.emplace_back(smap.object, smap.method_idx, value);
}
}
}
mtxLocker.unlock();
// Now invoke the methods.
for (const auto &p : toInvoke) {
ConfigStorePrivate::InvokeQtMethod(p.object, p.method_idx, p.value);
}
}