mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 19:20:16 +02:00
Eliminate file IO contentions for the settings
This commit is contained in:
parent
5805c0cf9e
commit
39dcd1f9bd
8 changed files with 186 additions and 45 deletions
|
@ -739,7 +739,7 @@ void MyAvatar::saveData() {
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
float loadSetting(QSettings& settings, const char* name, float defaultValue) {
|
float loadSetting(Settings& settings, const QString& name, float defaultValue) {
|
||||||
float value = settings.value(name, defaultValue).toFloat();
|
float value = settings.value(name, defaultValue).toFloat();
|
||||||
if (glm::isnan(value)) {
|
if (glm::isnan(value)) {
|
||||||
value = defaultValue;
|
value = defaultValue;
|
||||||
|
|
|
@ -10,11 +10,76 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "SettingHandle.h"
|
#include "SettingHandle.h"
|
||||||
|
#include "SettingManager.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const QString Settings::firstRun { "firstRun" };
|
const QString Settings::firstRun { "firstRun" };
|
||||||
|
|
||||||
|
Settings::Settings() :
|
||||||
|
_manager(DependencyManager::get<Setting::Manager>()),
|
||||||
|
_locker(&(_manager->getLock()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings::~Settings() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::remove(const QString& key) {
|
||||||
|
_manager->remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Settings::childGroups() const {
|
||||||
|
return _manager->childGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Settings::childKeys() const {
|
||||||
|
return _manager->childKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Settings::allKeys() const {
|
||||||
|
return _manager->allKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Settings::contains(const QString& key) const {
|
||||||
|
return _manager->contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Settings::beginReadArray(const QString & prefix) {
|
||||||
|
return _manager->beginReadArray(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::beginWriteArray(const QString& prefix, int size) {
|
||||||
|
_manager->beginWriteArray(prefix, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::endArray() {
|
||||||
|
_manager->endArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::setArrayIndex(int i) {
|
||||||
|
_manager->setArrayIndex(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::beginGroup(const QString& prefix) {
|
||||||
|
_manager->beginGroup(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::endGroup() {
|
||||||
|
_manager->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Settings::setValue(const QString& name, const QVariant& value) {
|
||||||
|
_manager->setValue(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant Settings::value(const QString& name, const QVariant& defaultValue) const {
|
||||||
|
return _manager->value(name, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Settings::getFloatValueIfValid(const QString& name, float& floatValue) {
|
void Settings::getFloatValueIfValid(const QString& name, float& floatValue) {
|
||||||
const QVariant badDefaultValue = NAN;
|
const QVariant badDefaultValue = NAN;
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
|
|
|
@ -14,19 +14,40 @@
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include <QSettings>
|
#include <QtCore/QSettings>
|
||||||
#include <QString>
|
#include <QtCore/QStack>
|
||||||
#include <QVariant>
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
#include <QtCore/QReadWriteLock>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
#include "SettingInterface.h"
|
#include "SettingInterface.h"
|
||||||
|
|
||||||
|
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
class Settings : public QSettings {
|
class Settings {
|
||||||
public:
|
public:
|
||||||
static const QString firstRun;
|
static const QString firstRun;
|
||||||
|
Settings();
|
||||||
|
~Settings();
|
||||||
|
|
||||||
|
void remove(const QString& key);
|
||||||
|
QStringList childGroups() const;
|
||||||
|
QStringList childKeys() const;
|
||||||
|
QStringList allKeys() const;
|
||||||
|
bool contains(const QString& key) const;
|
||||||
|
int beginReadArray(const QString & prefix);
|
||||||
|
void beginWriteArray(const QString& prefix, int size = -1);
|
||||||
|
void endArray();
|
||||||
|
void setArrayIndex(int i);
|
||||||
|
|
||||||
|
void beginGroup(const QString& prefix);
|
||||||
|
void endGroup();
|
||||||
|
|
||||||
|
void setValue(const QString& name, const QVariant& value);
|
||||||
|
QVariant value(const QString& name, const QVariant& defaultValue = QVariant()) const;
|
||||||
|
|
||||||
void getFloatValueIfValid(const QString& name, float& floatValue);
|
void getFloatValueIfValid(const QString& name, float& floatValue);
|
||||||
void getBoolValue(const QString& name, bool& boolValue);
|
void getBoolValue(const QString& name, bool& boolValue);
|
||||||
|
@ -36,6 +57,9 @@ public:
|
||||||
|
|
||||||
void setQuatValue(const QString& name, const glm::quat& quatValue);
|
void setQuatValue(const QString& name, const glm::quat& quatValue);
|
||||||
void getQuatValueIfValid(const QString& name, glm::quat& quatValue);
|
void getQuatValueIfValid(const QString& name, glm::quat& quatValue);
|
||||||
|
|
||||||
|
QSharedPointer<Setting::Manager> _manager;
|
||||||
|
QWriteLocker _locker;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Setting {
|
namespace Setting {
|
||||||
|
|
|
@ -9,27 +9,33 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "SettingInterface.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
#include "PathUtils.h"
|
#include "PathUtils.h"
|
||||||
#include "SettingInterface.h"
|
|
||||||
#include "SettingManager.h"
|
#include "SettingManager.h"
|
||||||
#include "SharedLogging.h"
|
#include "SharedLogging.h"
|
||||||
|
|
||||||
namespace Setting {
|
namespace Setting {
|
||||||
static Manager* privateInstance = nullptr;
|
static QSharedPointer<Manager> globalManager;
|
||||||
|
|
||||||
|
const QString Interface::FIRST_RUN { "firstRun" };
|
||||||
|
|
||||||
// cleans up the settings private instance. Should only be run once at closing down.
|
// cleans up the settings private instance. Should only be run once at closing down.
|
||||||
void cleanupPrivateInstance() {
|
void cleanupPrivateInstance() {
|
||||||
// grab the thread before we nuke the instance
|
// grab the thread before we nuke the instance
|
||||||
QThread* settingsManagerThread = privateInstance->thread();
|
QThread* settingsManagerThread = DependencyManager::get<Manager>()->thread();
|
||||||
|
|
||||||
// tell the private instance to clean itself up on its thread
|
// tell the private instance to clean itself up on its thread
|
||||||
privateInstance->deleteLater();
|
DependencyManager::destroy<Manager>();
|
||||||
privateInstance = NULL;
|
|
||||||
|
//
|
||||||
|
globalManager->deleteLater();
|
||||||
|
globalManager.reset();
|
||||||
|
|
||||||
// quit the settings manager thread and wait on it to make sure it's gone
|
// quit the settings manager thread and wait on it to make sure it's gone
|
||||||
settingsManagerThread->quit();
|
settingsManagerThread->quit();
|
||||||
|
@ -63,14 +69,13 @@ namespace Setting {
|
||||||
QThread* thread = new QThread();
|
QThread* thread = new QThread();
|
||||||
Q_CHECK_PTR(thread);
|
Q_CHECK_PTR(thread);
|
||||||
thread->setObjectName("Settings Thread");
|
thread->setObjectName("Settings Thread");
|
||||||
|
|
||||||
privateInstance = new Manager();
|
|
||||||
Q_CHECK_PTR(privateInstance);
|
|
||||||
|
|
||||||
QObject::connect(privateInstance, SIGNAL(destroyed()), thread, SLOT(quit()));
|
globalManager = DependencyManager::set<Manager>();
|
||||||
QObject::connect(thread, SIGNAL(started()), privateInstance, SLOT(startTimer()));
|
|
||||||
|
QObject::connect(globalManager.data(), SIGNAL(destroyed()), thread, SLOT(quit()));
|
||||||
|
QObject::connect(thread, SIGNAL(started()), globalManager.data(), SLOT(startTimer()));
|
||||||
QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
||||||
privateInstance->moveToThread(thread);
|
globalManager->moveToThread(thread);
|
||||||
thread->start();
|
thread->start();
|
||||||
qCDebug(shared) << "Settings thread started.";
|
qCDebug(shared) << "Settings thread started.";
|
||||||
|
|
||||||
|
@ -79,7 +84,7 @@ namespace Setting {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::init() {
|
void Interface::init() {
|
||||||
if (!privateInstance) {
|
if (!DependencyManager::isSet<Manager>()) {
|
||||||
// WARNING: As long as we are using QSettings this should always be triggered for each Setting::Handle
|
// WARNING: As long as we are using QSettings this should always be triggered for each Setting::Handle
|
||||||
// in an assignment-client - the QSettings backing we use for this means persistence of these
|
// in an assignment-client - the QSettings backing we use for this means persistence of these
|
||||||
// settings from an AC (when there can be multiple terminating at same time on one machine)
|
// settings from an AC (when there can be multiple terminating at same time on one machine)
|
||||||
|
@ -87,9 +92,13 @@ namespace Setting {
|
||||||
qWarning() << "Setting::Interface::init() for key" << _key << "- Manager not yet created." <<
|
qWarning() << "Setting::Interface::init() for key" << _key << "- Manager not yet created." <<
|
||||||
"Settings persistence disabled.";
|
"Settings persistence disabled.";
|
||||||
} else {
|
} else {
|
||||||
// Register Handle
|
_manager = DependencyManager::get<Manager>();
|
||||||
privateInstance->registerHandle(this);
|
auto manager = _manager.lock();
|
||||||
_isInitialized = true;
|
if (manager) {
|
||||||
|
// Register Handle
|
||||||
|
manager->registerHandle(this);
|
||||||
|
_isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Load value from disk
|
// Load value from disk
|
||||||
load();
|
load();
|
||||||
|
@ -97,11 +106,13 @@ namespace Setting {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::deinit() {
|
void Interface::deinit() {
|
||||||
if (_isInitialized && privateInstance) {
|
if (_isInitialized && _manager) {
|
||||||
// Save value to disk
|
auto manager = _manager.lock();
|
||||||
save();
|
if (manager) {
|
||||||
|
// Save value to disk
|
||||||
privateInstance->removeHandle(_key);
|
save();
|
||||||
|
manager->removeHandle(_key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,14 +124,16 @@ namespace Setting {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::save() {
|
void Interface::save() {
|
||||||
if (privateInstance) {
|
auto manager = _manager.lock();
|
||||||
privateInstance->saveSetting(this);
|
if (manager) {
|
||||||
|
manager->saveSetting(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::load() {
|
void Interface::load() {
|
||||||
if (privateInstance) {
|
auto manager = _manager.lock();
|
||||||
privateInstance->loadSetting(this);
|
if (manager) {
|
||||||
|
manager->loadSetting(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
#ifndef hifi_SettingInterface_h
|
#ifndef hifi_SettingInterface_h
|
||||||
#define hifi_SettingInterface_h
|
#define hifi_SettingInterface_h
|
||||||
|
|
||||||
#include <QString>
|
#include <memory>
|
||||||
#include <QVariant>
|
#include <QtCore/QWeakPointer>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
|
||||||
namespace Setting {
|
namespace Setting {
|
||||||
void preInit();
|
void preInit();
|
||||||
|
@ -22,6 +24,8 @@ namespace Setting {
|
||||||
|
|
||||||
class Interface {
|
class Interface {
|
||||||
public:
|
public:
|
||||||
|
static const QString FIRST_RUN;
|
||||||
|
|
||||||
QString getKey() const { return _key; }
|
QString getKey() const { return _key; }
|
||||||
bool isSet() const { return _isSet; }
|
bool isSet() const { return _isSet; }
|
||||||
|
|
||||||
|
@ -44,6 +48,8 @@ namespace Setting {
|
||||||
const QString _key;
|
const QString _key;
|
||||||
|
|
||||||
friend class Manager;
|
friend class Manager;
|
||||||
|
|
||||||
|
QWeakPointer<Manager> _manager;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,15 @@
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <QThread>
|
#include <QtCore/QThread>
|
||||||
#include <QDebug>
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QUuid>
|
||||||
|
|
||||||
#include "SettingInterface.h"
|
#include "SettingInterface.h"
|
||||||
#include "SettingManager.h"
|
#include "SettingManager.h"
|
||||||
|
|
||||||
namespace Setting {
|
namespace Setting {
|
||||||
|
|
||||||
Manager::~Manager() {
|
Manager::~Manager() {
|
||||||
// Cleanup timer
|
// Cleanup timer
|
||||||
stopTimer();
|
stopTimer();
|
||||||
|
@ -27,6 +29,10 @@ namespace Setting {
|
||||||
// sync will be called in the QSettings destructor
|
// sync will be called in the QSettings destructor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom deleter does nothing, because we need to shutdown later than the dependency manager
|
||||||
|
void Manager::customDeleter() { }
|
||||||
|
|
||||||
|
|
||||||
void Manager::registerHandle(Setting::Interface* handle) {
|
void Manager::registerHandle(Setting::Interface* handle) {
|
||||||
QString key = handle->getKey();
|
QString key = handle->getKey();
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
|
@ -47,12 +53,17 @@ namespace Setting {
|
||||||
handle->setVariant(value(handle->getKey()));
|
handle->setVariant(value(handle->getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Manager::saveSetting(Interface* handle) {
|
void Manager::saveSetting(Interface* handle) {
|
||||||
|
auto key = handle->getKey();
|
||||||
|
QVariant handleValue = UNSET_VALUE;
|
||||||
if (handle->isSet()) {
|
if (handle->isSet()) {
|
||||||
setValue(handle->getKey(), handle->getVariant());
|
handleValue = handle->getVariant();
|
||||||
} else {
|
|
||||||
remove(handle->getKey());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withWriteLock([&] {
|
||||||
|
_pendingChanges[key] = handleValue;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int SAVE_INTERVAL_MSEC = 5 * 1000; // 5 sec
|
static const int SAVE_INTERVAL_MSEC = 5 * 1000; // 5 sec
|
||||||
|
@ -74,12 +85,24 @@ namespace Setting {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::saveAll() {
|
void Manager::saveAll() {
|
||||||
withReadLock([&] {
|
QHash<QString, QVariant> newPendingChanges;
|
||||||
for (auto handle : _handles) {
|
withWriteLock([&] {
|
||||||
saveSetting(handle);
|
newPendingChanges.swap(_pendingChanges);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (auto key : newPendingChanges.keys()) {
|
||||||
|
auto newValue = newPendingChanges[key];
|
||||||
|
auto savedValue = value(key, UNSET_VALUE);
|
||||||
|
if (newValue == savedValue) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (newValue == UNSET_VALUE) {
|
||||||
|
remove(key);
|
||||||
|
} else {
|
||||||
|
setValue(key, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Restart timer
|
// Restart timer
|
||||||
if (_saveTimer) {
|
if (_saveTimer) {
|
||||||
_saveTimer->start();
|
_saveTimer->start();
|
||||||
|
|
|
@ -12,17 +12,23 @@
|
||||||
#ifndef hifi_SettingManager_h
|
#ifndef hifi_SettingManager_h
|
||||||
#define hifi_SettingManager_h
|
#define hifi_SettingManager_h
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QtCore/QPointer>
|
||||||
#include <QSettings>
|
#include <QtCore/QSettings>
|
||||||
#include <QTimer>
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtCore/QUuid>
|
||||||
|
|
||||||
|
#include "DependencyManager.h"
|
||||||
#include "shared/ReadWriteLockable.h"
|
#include "shared/ReadWriteLockable.h"
|
||||||
|
|
||||||
namespace Setting {
|
namespace Setting {
|
||||||
class Interface;
|
class Interface;
|
||||||
|
|
||||||
class Manager : public QSettings, public ReadWriteLockable {
|
class Manager : public QSettings, public ReadWriteLockable, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
void customDeleter() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
~Manager();
|
~Manager();
|
||||||
void registerHandle(Interface* handle);
|
void registerHandle(Interface* handle);
|
||||||
|
@ -40,6 +46,8 @@ namespace Setting {
|
||||||
private:
|
private:
|
||||||
QHash<QString, Interface*> _handles;
|
QHash<QString, Interface*> _handles;
|
||||||
QPointer<QTimer> _saveTimer = nullptr;
|
QPointer<QTimer> _saveTimer = nullptr;
|
||||||
|
const QVariant UNSET_VALUE { QUuid::createUuid().variant() };
|
||||||
|
QHash<QString, QVariant> _pendingChanges;
|
||||||
|
|
||||||
friend class Interface;
|
friend class Interface;
|
||||||
friend void cleanupPrivateInstance();
|
friend void cleanupPrivateInstance();
|
||||||
|
|
|
@ -45,6 +45,8 @@ public:
|
||||||
template <typename F>
|
template <typename F>
|
||||||
bool withTryReadLock(F&& f, int timeout) const;
|
bool withTryReadLock(F&& f, int timeout) const;
|
||||||
|
|
||||||
|
QReadWriteLock& getLock() const { return _lock; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable QReadWriteLock _lock { QReadWriteLock::Recursive };
|
mutable QReadWriteLock _lock { QReadWriteLock::Recursive };
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue