refs #494 Using CValueCachePacket, cache values are given timestamps.

This commit is contained in:
Mathew Sutcliffe
2015-10-22 22:37:45 +01:00
parent eb11b69c6d
commit 966eed9044
13 changed files with 99 additions and 64 deletions

View File

@@ -63,12 +63,12 @@ namespace BlackCore
this->m_logSubscriptions[subscriber].removeAll(pattern); this->m_logSubscriptions[subscriber].removeAll(pattern);
}); });
connect(CSettingsCache::instance(), &CSettingsCache::valuesChangedByLocal, [this](const CVariantMap &settings) connect(CSettingsCache::instance(), &CSettingsCache::valuesChangedByLocal, [this](const CValueCachePacket &settings)
{ {
this->changeSettings(settings, {}); this->changeSettings(settings, {});
}); });
connect(this, &IContextApplication::settingsChanged, [](const CVariantMap &settings, const CIdentifier &origin) connect(this, &IContextApplication::settingsChanged, [](const CValueCachePacket &settings, const CIdentifier &origin)
{ {
// Intentionally don't check for round trip here // Intentionally don't check for round trip here
CSettingsCache::instance()->changeValuesFromRemote(settings, origin); CSettingsCache::instance()->changeValuesFromRemote(settings, origin);
@@ -120,7 +120,7 @@ namespace BlackCore
return result; return result;
} }
void IContextApplication::changeSettings(const CVariantMap &settings, const CIdentifier &origin) void IContextApplication::changeSettings(const CValueCachePacket &settings, const CIdentifier &origin)
{ {
Q_UNUSED(settings); Q_UNUSED(settings);
Q_UNUSED(origin); Q_UNUSED(origin);

View File

@@ -95,7 +95,7 @@ namespace BlackCore
//! One or more settings were changed //! One or more settings were changed
//! \note Used for cache relay, do not use directly //! \note Used for cache relay, do not use directly
void settingsChanged(const BlackMisc::CVariantMap &settings, const BlackMisc::CIdentifier &origin); void settingsChanged(const BlackMisc::CValueCachePacket &settings, const BlackMisc::CIdentifier &origin);
//! New action was registered //! New action was registered
//! \note Used to register hotkey action, do not use directly //! \note Used to register hotkey action, do not use directly
@@ -138,10 +138,10 @@ namespace BlackCore
//! Ratify some settings changed by another process //! Ratify some settings changed by another process
//! \note Not pure because it can be called from the base class constructor. //! \note Not pure because it can be called from the base class constructor.
//! \note This is the function which relays cache changes via DBus. //! \note This is the function which relays cache changes via DBus.
virtual void changeSettings(const BlackMisc::CVariantMap &settings, const BlackMisc::CIdentifier &origin); virtual void changeSettings(const BlackMisc::CValueCachePacket &settings, const BlackMisc::CIdentifier &origin);
//! Get all settings currently in core settings cache //! Get all settings currently in core settings cache
virtual BlackMisc::CVariantMap getAllSettings() const = 0; virtual BlackMisc::CValueCachePacket getAllSettings() const = 0;
//! Update local settings with settings from core //! Update local settings with settings from core
virtual void synchronizeLocalSettings() = 0; virtual void synchronizeLocalSettings() = 0;

View File

@@ -66,16 +66,16 @@ namespace BlackCore
// no-op: proxy implements this method by calling getAllLogSubscriptions // no-op: proxy implements this method by calling getAllLogSubscriptions
} }
void CContextApplication::changeSettings(const CVariantMap &settings, const CIdentifier &origin) void CContextApplication::changeSettings(const CValueCachePacket &settings, const CIdentifier &origin)
{ {
// Intentionally don't check for round trip here // Intentionally don't check for round trip here
emit this->settingsChanged(settings, origin); emit this->settingsChanged(settings, origin);
} }
BlackMisc::CVariantMap CContextApplication::getAllSettings() const BlackMisc::CValueCachePacket CContextApplication::getAllSettings() const
{ {
if (m_debugEnabled) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO; } if (m_debugEnabled) { CLogMessage(this, CLogCategory::contextSlot()).debug() << Q_FUNC_INFO; }
return CSettingsCache::instance()->getAllValues(); return CSettingsCache::instance()->getAllValuesWithTimestamps();
} }
void CContextApplication::synchronizeLocalSettings() void CContextApplication::synchronizeLocalSettings()

View File

@@ -45,10 +45,10 @@ namespace BlackCore
virtual void synchronizeLogSubscriptions(); virtual void synchronizeLogSubscriptions();
//! \copydoc IContextApplication::changeSettings //! \copydoc IContextApplication::changeSettings
virtual void changeSettings(const BlackMisc::CVariantMap &settings, const BlackMisc::CIdentifier &origin) override; virtual void changeSettings(const BlackMisc::CValueCachePacket &settings, const BlackMisc::CIdentifier &origin) override;
//! \copydoc IContextApplication::getAllSettings //! \copydoc IContextApplication::getAllSettings
virtual BlackMisc::CVariantMap getAllSettings() const override; virtual BlackMisc::CValueCachePacket getAllSettings() const override;
//! \copydoc IContextApplication::synchronizeLocalSettings //! \copydoc IContextApplication::synchronizeLocalSettings
virtual void synchronizeLocalSettings() override; virtual void synchronizeLocalSettings() override;

View File

@@ -50,7 +50,7 @@ namespace BlackCore
"logSubscriptionRemoved", this, SIGNAL(logSubscriptionRemoved(BlackMisc::CIdentifier, BlackMisc::CLogPattern))); "logSubscriptionRemoved", this, SIGNAL(logSubscriptionRemoved(BlackMisc::CIdentifier, BlackMisc::CLogPattern)));
Q_ASSERT(s); Q_ASSERT(s);
s = connection.connect(serviceName, IContextApplication::ObjectPath(), IContextApplication::InterfaceName(), s = connection.connect(serviceName, IContextApplication::ObjectPath(), IContextApplication::InterfaceName(),
"settingsChanged", this, SIGNAL(settingsChanged(BlackMisc::CVariantMap, BlackMisc::CIdentifier))); "settingsChanged", this, SIGNAL(settingsChanged(BlackMisc::CValueCachePacket, BlackMisc::CIdentifier)));
Q_ASSERT(s); Q_ASSERT(s);
s = connection.connect(serviceName, IContextApplication::ObjectPath(), IContextApplication::InterfaceName(), s = connection.connect(serviceName, IContextApplication::ObjectPath(), IContextApplication::InterfaceName(),
"registrationChanged", this, SIGNAL(registrationChanged())); "registrationChanged", this, SIGNAL(registrationChanged()));
@@ -93,14 +93,14 @@ namespace BlackCore
for (const auto &pattern : CLogHandler::instance()->getAllSubscriptions()) { this->addLogSubscription({}, pattern); } for (const auto &pattern : CLogHandler::instance()->getAllSubscriptions()) { this->addLogSubscription({}, pattern); }
} }
void CContextApplicationProxy::changeSettings(const CVariantMap &settings, const CIdentifier &origin) void CContextApplicationProxy::changeSettings(const CValueCachePacket &settings, const CIdentifier &origin)
{ {
this->m_dBusInterface->callDBus(QLatin1Literal("changeSettings"), settings, origin); this->m_dBusInterface->callDBus(QLatin1Literal("changeSettings"), settings, origin);
} }
BlackMisc::CVariantMap CContextApplicationProxy::getAllSettings() const BlackMisc::CValueCachePacket CContextApplicationProxy::getAllSettings() const
{ {
return this->m_dBusInterface->callDBusRet<BlackMisc::CVariantMap>(QLatin1Literal("getAllSettings")); return this->m_dBusInterface->callDBusRet<BlackMisc::CValueCachePacket>(QLatin1Literal("getAllSettings"));
} }
void CContextApplicationProxy::synchronizeLocalSettings() void CContextApplicationProxy::synchronizeLocalSettings()

View File

@@ -41,10 +41,10 @@ namespace BlackCore
virtual void synchronizeLogSubscriptions(); virtual void synchronizeLogSubscriptions();
//! \copydoc IContextApplication::changeSettings //! \copydoc IContextApplication::changeSettings
virtual void changeSettings(const BlackMisc::CVariantMap &settings, const BlackMisc::CIdentifier &origin) override; virtual void changeSettings(const BlackMisc::CValueCachePacket &settings, const BlackMisc::CIdentifier &origin) override;
//! \copydoc IContextApplication::getAllSettings //! \copydoc IContextApplication::getAllSettings
virtual BlackMisc::CVariantMap getAllSettings() const override; virtual BlackMisc::CValueCachePacket getAllSettings() const override;
//! \copydoc IContextApplication::synchronizeLocalSettings //! \copydoc IContextApplication::synchronizeLocalSettings
virtual void synchronizeLocalSettings() override; virtual void synchronizeLocalSettings() override;

View File

@@ -26,7 +26,7 @@ namespace BlackCore
CLogMessage(this).error("Failed to create directory %1") << persistentStore(); CLogMessage(this).error("Failed to create directory %1") << persistentStore();
} }
connect(this, &CValueCache::valuesChangedByLocal, this, &CDataCache::saveToStore); connect(this, &CValueCache::valuesChangedByLocal, this, [this](const CValueCachePacket &values) { saveToStore(values.toVariantMap()); });
connect(&m_watcher, &QFileSystemWatcher::fileChanged, this, [this] { loadFromStore(); }); connect(&m_watcher, &QFileSystemWatcher::fileChanged, this, [this] { loadFromStore(); });
if (! QFile::exists(m_revisionFileName)) { QFile(m_revisionFileName).open(QFile::WriteOnly); } if (! QFile::exists(m_revisionFileName)) { QFile(m_revisionFileName).open(QFile::WriteOnly); }
@@ -116,7 +116,7 @@ namespace BlackCore
if (m_revision != newRevision) if (m_revision != newRevision)
{ {
m_revision = newRevision; m_revision = newRevision;
CVariantMap newValues; CValueCachePacket newValues;
loadFromFiles(persistentStore(), newValues); loadFromFiles(persistentStore(), newValues);
m_deferredChanges.insert(newValues); m_deferredChanges.insert(newValues);
} }

View File

@@ -49,7 +49,7 @@ namespace BlackCore
QFileSystemWatcher m_watcher; QFileSystemWatcher m_watcher;
QUuid m_revision; QUuid m_revision;
const QString m_revisionFileName { persistentStore() + "/.rev" }; const QString m_revisionFileName { persistentStore() + "/.rev" };
BlackMisc::CVariantMap m_deferredChanges; BlackMisc::CValueCachePacket m_deferredChanges;
}; };
/*! /*!

View File

@@ -462,7 +462,7 @@ namespace BlackMisc
{ {
if (it1.key() < it2.key()) { ++it1; } if (it1.key() < it2.key()) { ++it1; }
else if (it2.key() < it1.key()) { ++it2; } else if (it2.key() < it1.key()) { ++it2; }
else { functor(it1.key(), it1.value(), it2.value()); ++it1; ++it2; } else { functor(it1.key(), it1.value(), it2); ++it1; ++it2; }
} }
} }

View File

@@ -71,7 +71,7 @@ namespace BlackMisc
if (mode == LocalOnly) if (mode == LocalOnly)
{ {
// loopback signal to own slot for local operation // loopback signal to own slot for local operation
connect(this, &CValueCache::valuesChangedByLocal, this, [ = ](const CVariantMap &values) connect(this, &CValueCache::valuesChangedByLocal, this, [ = ](const CValueCachePacket &values)
{ {
changeValuesFromRemote(values, CIdentifier()); changeValuesFromRemote(values, CIdentifier());
}); });
@@ -84,6 +84,7 @@ namespace BlackMisc
const QString m_key; const QString m_key;
CVariant m_value; CVariant m_value;
int m_pendingChanges = 0; int m_pendingChanges = 0;
std::atomic<qint64> m_timestamp { QDateTime::currentMSecsSinceEpoch() };
}; };
CValueCache::Element &CValueCache::getElement(const QString &key) CValueCache::Element &CValueCache::getElement(const QString &key)
@@ -100,10 +101,11 @@ namespace BlackMisc
return **m_elements.insert(pos, key, ElementPtr(new Element(key))); return **m_elements.insert(pos, key, ElementPtr(new Element(key)));
} }
CVariant CValueCache::getValue(const QString &key) std::pair<CVariant, qint64> CValueCache::getValue(const QString &key)
{ {
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
return getElement(key).m_value; const auto &element = getElement(key);
return std::make_pair(element.m_value, element.m_timestamp.load());
} }
CVariantMap CValueCache::getAllValues(const QString &keyPrefix) const CVariantMap CValueCache::getAllValues(const QString &keyPrefix) const
@@ -117,13 +119,24 @@ namespace BlackMisc
return map; return map;
} }
void CValueCache::insertValues(const CVariantMap &values) CValueCachePacket CValueCache::getAllValuesWithTimestamps(const QString &keyPrefix) const
{
QMutexLocker lock(&m_mutex);
CValueCachePacket map;
for (const auto &element : makeRange(m_elements.lowerBound(keyPrefix), m_elements.lowerBound(keyPrefix + QChar(QChar::LastValidCodePoint))))
{
map.insert(element->m_key, element->m_value, element->m_timestamp);
}
return map;
}
void CValueCache::insertValues(const CValueCachePacket &values)
{ {
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
changeValues(values); changeValues(values);
} }
void CValueCache::changeValues(const CVariantMap &values) void CValueCache::changeValues(const CValueCachePacket &values)
{ {
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
if (values.empty()) { return; } if (values.empty()) { return; }
@@ -137,16 +150,17 @@ namespace BlackMisc
Q_ASSERT(isSafeToIncrement(element.m_pendingChanges)); Q_ASSERT(isSafeToIncrement(element.m_pendingChanges));
element.m_pendingChanges++; element.m_pendingChanges++;
element.m_value = in.value(); element.m_value = in.value();
element.m_timestamp = in.timestamp();
} }
emit valuesChanged(values, sender()); emit valuesChanged(values, sender());
emit valuesChangedByLocal(values); emit valuesChangedByLocal(values);
} }
void CValueCache::changeValuesFromRemote(const CVariantMap &values, const CIdentifier &originator) void CValueCache::changeValuesFromRemote(const CValueCachePacket &values, const CIdentifier &originator)
{ {
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
if (values.empty()) { return; } if (values.empty()) { return; }
CVariantMap ratifiedChanges; CValueCachePacket ratifiedChanges;
auto out = m_elements.lowerBound(values.cbegin().key()); auto out = m_elements.lowerBound(values.cbegin().key());
auto end = m_elements.upperBound((values.cend() - 1).key()); auto end = m_elements.upperBound((values.cend() - 1).key());
for (auto in = values.cbegin(); in != values.cend(); ++in) for (auto in = values.cbegin(); in != values.cend(); ++in)
@@ -162,7 +176,8 @@ namespace BlackMisc
else if (element.m_pendingChanges == 0) // ratify a change only if own change is not pending, to ensure consistency else if (element.m_pendingChanges == 0) // ratify a change only if own change is not pending, to ensure consistency
{ {
element.m_value = in.value(); element.m_value = in.value();
ratifiedChanges.insert(in.key(), in.value()); element.m_timestamp = in.timestamp();
ratifiedChanges.insert(in.key(), in.value(), in.timestamp());
} }
} }
if (! ratifiedChanges.empty()) if (! ratifiedChanges.empty())
@@ -180,7 +195,7 @@ namespace BlackMisc
{ {
CVariantMap map; CVariantMap map;
map.convertFromJson(json); map.convertFromJson(json);
insertValues(map); insertValues({ map, QDateTime::currentMSecsSinceEpoch() });
} }
CStatusMessage CValueCache::saveToFiles(const QString &dir, const QString &keyPrefix) const CStatusMessage CValueCache::saveToFiles(const QString &dir, const QString &keyPrefix) const
@@ -229,13 +244,13 @@ namespace BlackMisc
CStatusMessage CValueCache::loadFromFiles(const QString &dir) CStatusMessage CValueCache::loadFromFiles(const QString &dir)
{ {
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
CVariantMap values; CValueCachePacket values;
auto status = loadFromFiles(dir, values); auto status = loadFromFiles(dir, values);
insertValues(values); insertValues(values);
return status; return status;
} }
CStatusMessage CValueCache::loadFromFiles(const QString &dir, CVariantMap &o_values) const CStatusMessage CValueCache::loadFromFiles(const QString &dir, CValueCachePacket &o_values) const
{ {
QMutexLocker lock(&m_mutex); QMutexLocker lock(&m_mutex);
if (! QDir(dir).isReadable()) if (! QDir(dir).isReadable())
@@ -258,7 +273,7 @@ namespace BlackMisc
CVariantMap temp; CVariantMap temp;
temp.convertFromJson(json.object()); temp.convertFromJson(json.object());
temp.removeDuplicates(currentValues); temp.removeDuplicates(currentValues);
o_values.insert(temp); o_values.insert(temp, QFileInfo(file).lastModified().toMSecsSinceEpoch());
} }
return {}; return {};
} }
@@ -309,6 +324,7 @@ namespace BlackMisc
{} {}
const QString m_key; const QString m_key;
LockFree<CVariant> m_value; LockFree<CVariant> m_value;
std::atomic<qint64> m_timestamp { QDateTime::currentMSecsSinceEpoch() };
const int m_metaType = QMetaType::UnknownType; const int m_metaType = QMetaType::UnknownType;
const Validator m_validator; const Validator m_validator;
const CVariant m_default; const CVariant m_default;
@@ -323,7 +339,7 @@ namespace BlackMisc
Q_ASSERT_X(defaultValue.isValid() && validator ? validator(defaultValue) : true, "CValuePage", "Validator rejects default value"); Q_ASSERT_X(defaultValue.isValid() && validator ? validator(defaultValue) : true, "CValuePage", "Validator rejects default value");
auto &element = *(m_elements[key] = ElementPtr(new Element(key, metaType, validator, defaultValue, slot))); auto &element = *(m_elements[key] = ElementPtr(new Element(key, metaType, validator, defaultValue, slot)));
element.m_value.uniqueWrite() = m_cache->getValue(key); std::forward_as_tuple(element.m_value.uniqueWrite(), element.m_timestamp) = m_cache->getValue(key);
auto error = validate(element, element.m_value.read()); auto error = validate(element, element.m_value.read());
if (! error.isEmpty()) if (! error.isEmpty())
@@ -358,7 +374,7 @@ namespace BlackMisc
element.m_pendingChanges++; element.m_pendingChanges++;
element.m_value.uniqueWrite() = value; element.m_value.uniqueWrite() = value;
emit valuesWantToCache({ { element.m_key, value } }); emit valuesWantToCache({ { { element.m_key, value } }, QDateTime::currentMSecsSinceEpoch() });
} }
} }
else else
@@ -369,13 +385,18 @@ namespace BlackMisc
return error; return error;
} }
void CValuePage::setValuesFromCache(const CVariantMap &values, QObject *changedBy) qint64 CValuePage::getTimestamp(const Element &element) const
{
return element.m_timestamp;
}
void CValuePage::setValuesFromCache(const CValueCachePacket &values, QObject *changedBy)
{ {
Q_ASSERT(QThread::currentThread() == thread()); Q_ASSERT(QThread::currentThread() == thread());
QList<NotifySlot> notifySlots; QList<NotifySlot> notifySlots;
forEachIntersection(m_elements, values, [changedBy, this, &notifySlots](const QString &, const ElementPtr &element, const CVariant &value) forEachIntersection(m_elements, values, [changedBy, this, &notifySlots](const QString &, const ElementPtr &element, CValueCachePacket::const_iterator it)
{ {
if (changedBy == this) // round trip if (changedBy == this) // round trip
{ {
@@ -384,10 +405,11 @@ namespace BlackMisc
} }
else if (element->m_pendingChanges == 0) // ratify a change only if own change is not pending, to ensure consistency else if (element->m_pendingChanges == 0) // ratify a change only if own change is not pending, to ensure consistency
{ {
auto error = validate(*element, value); auto error = validate(*element, it.value());
if (error.isEmpty()) if (error.isEmpty())
{ {
element->m_value.uniqueWrite() = value; element->m_value.uniqueWrite() = it.value();
element->m_timestamp = it.timestamp();
if (element->m_notifySlot && ! notifySlots.contains(element->m_notifySlot)) { notifySlots.push_back(element->m_notifySlot); } if (element->m_notifySlot && ! notifySlots.contains(element->m_notifySlot)) { notifySlots.push_back(element->m_notifySlot); }
} }
else else
@@ -426,13 +448,15 @@ namespace BlackMisc
if (m_batchMode <= 0 && ! m_batchedValues.empty()) if (m_batchMode <= 0 && ! m_batchedValues.empty())
{ {
forEachIntersection(m_elements, m_batchedValues, [](const QString &, const ElementPtr &element, const CVariant &value) qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
forEachIntersection(m_elements, m_batchedValues, [timestamp](const QString &, const ElementPtr &element, CVariantMap::const_iterator it)
{ {
Q_ASSERT(isSafeToIncrement(element->m_pendingChanges)); Q_ASSERT(isSafeToIncrement(element->m_pendingChanges));
element->m_pendingChanges++; element->m_pendingChanges++;
element->m_value.uniqueWrite() = value; element->m_value.uniqueWrite() = it.value();
element->m_timestamp = timestamp;
}); });
emit valuesWantToCache(m_batchedValues); emit valuesWantToCache({ m_batchedValues, timestamp });
} }
} }

View File

@@ -92,10 +92,14 @@ namespace BlackMisc
//! \threadsafe //! \threadsafe
BlackMisc::CVariantMap getAllValues(const QString &keyPrefix = {}) const; BlackMisc::CVariantMap getAllValues(const QString &keyPrefix = {}) const;
//! Return map containing all values in the cache, and timestamps when they were modified.
//! \threadsafe
BlackMisc::CValueCachePacket getAllValuesWithTimestamps(const QString &keyPrefix = {}) const;
//! Add some values to the cache. //! Add some values to the cache.
//! Values already in the cache will remain in the cache unless they are overwritten. //! Values already in the cache will remain in the cache unless they are overwritten.
//! \threadsafe //! \threadsafe
void insertValues(const BlackMisc::CVariantMap &values); void insertValues(const BlackMisc::CValueCachePacket &values);
//! Save values in Json format. //! Save values in Json format.
//! If prefix is provided then only those values whose keys start with that prefix. //! If prefix is provided then only those values whose keys start with that prefix.
@@ -131,20 +135,20 @@ namespace BlackMisc
//! \see BlackMisc::CValueCache::valuesChangedByLocal. //! \see BlackMisc::CValueCache::valuesChangedByLocal.
//! \param values The values that were changed. //! \param values The values that were changed.
//! \param originator Identifier of the process which made the change. Can be this very process, or a different one. //! \param originator Identifier of the process which made the change. Can be this very process, or a different one.
void changeValuesFromRemote(const BlackMisc::CVariantMap &values, const BlackMisc::CIdentifier &originator); void changeValuesFromRemote(const BlackMisc::CValueCachePacket &values, const BlackMisc::CIdentifier &originator);
signals: signals:
//! Emitted when values in the cache are changed by an object in this very process. //! Emitted when values in the cache are changed by an object in this very process.
//! The interprocess communication (e.g. DBus) should arrange for this signal to call the slot changeValueFromRemote //! The interprocess communication (e.g. DBus) should arrange for this signal to call the slot changeValueFromRemote
//! of CValueCache instances in all processes including this one. The slot will do its own round-trip detection. //! of CValueCache instances in all processes including this one. The slot will do its own round-trip detection.
void valuesChangedByLocal(const BlackMisc::CVariantMap &values); void valuesChangedByLocal(const BlackMisc::CValueCachePacket &values);
protected: protected:
//! Save specific values to Json files in a given directory. //! Save specific values to Json files in a given directory.
CStatusMessage saveToFiles(const QString &directory, const CVariantMap &values) const; CStatusMessage saveToFiles(const QString &directory, const CVariantMap &values) const;
//! Load from Json files in a given directory any values which differ from the current ones, and insert them in o_values. //! Load from Json files in a given directory any values which differ from the current ones, and insert them in o_values.
CStatusMessage loadFromFiles(const QString &directory, CVariantMap &o_values) const; CStatusMessage loadFromFiles(const QString &directory, CValueCachePacket &o_values) const;
//! Mutex protecting operations which are critical on m_elements. //! Mutex protecting operations which are critical on m_elements.
mutable QMutex m_mutex { QMutex::Recursive }; mutable QMutex m_mutex { QMutex::Recursive };
@@ -158,14 +162,14 @@ namespace BlackMisc
Element &getElement(const QString &key); Element &getElement(const QString &key);
Element &getElement(const QString &key, QMap<QString, ElementPtr>::const_iterator pos); Element &getElement(const QString &key, QMap<QString, ElementPtr>::const_iterator pos);
CVariant getValue(const QString &key); std::pair<CVariant, qint64> getValue(const QString &key);
signals: signals:
//! \private //! \private
void valuesChanged(const BlackMisc::CVariantMap &values, QObject *changedBy); void valuesChanged(const BlackMisc::CValueCachePacket &values, QObject *changedBy);
private slots: private slots:
void changeValues(const BlackMisc::CVariantMap &values); void changeValues(const BlackMisc::CValueCachePacket &values);
}; };
/*! /*!
@@ -211,6 +215,9 @@ namespace BlackMisc
//! Write a new value. Must be called from the thread in which the owner lives. //! Write a new value. Must be called from the thread in which the owner lives.
CStatusMessage set(const T &value) { return m_page.setValue(m_element, CVariant::from(value)); } CStatusMessage set(const T &value) { return m_page.setValue(m_element, CVariant::from(value)); }
//! Return the time when this value was updated.
QDateTime getTimestamp() const { return m_page.getTimestamp(m_element); }
//! Deleted copy constructor. //! Deleted copy constructor.
CCached(const CCached &) = delete; CCached(const CCached &) = delete;

View File

@@ -22,6 +22,7 @@ namespace BlackMisc
{ {
class CIdentifier; class CIdentifier;
class CValueCache; class CValueCache;
class CValueCachePacket;
namespace Private namespace Private
{ {
@@ -63,11 +64,14 @@ namespace BlackMisc
//! Write the value corresponding to the element's key and begin synchronizing it to any other pages. //! Write the value corresponding to the element's key and begin synchronizing it to any other pages.
CStatusMessage setValue(Element &element, const CVariant &value); CStatusMessage setValue(Element &element, const CVariant &value);
//! Get the timestamp corresponding to the element.
qint64 getTimestamp(const Element &element) const;
//! Synchronize with a change caused by another page. //! Synchronize with a change caused by another page.
//! Connected to signal CValueCache::valuesChanged. //! Connected to signal CValueCache::valuesChanged.
//! \param values The new values. //! \param values The new values.
//! \param changedBy Pointer to the CValuePage which caused the change. Null if it was changed by another process. //! \param changedBy Pointer to the CValuePage which caused the change. Null if it was changed by another process.
void setValuesFromCache(const BlackMisc::CVariantMap &values, QObject *changedBy); void setValuesFromCache(const BlackMisc::CValueCachePacket &values, QObject *changedBy);
//! Put this page into batching mode. //! Put this page into batching mode.
void beginBatch(); void beginBatch();
@@ -81,7 +85,7 @@ namespace BlackMisc
signals: signals:
//! Synchronize this page's changes to other pages. //! Synchronize this page's changes to other pages.
//! Connected to slot CValueCache::changeValues. //! Connected to slot CValueCache::changeValues.
void valuesWantToCache(const BlackMisc::CVariantMap &values); void valuesWantToCache(const BlackMisc::CValueCachePacket &values);
private: private:
using ElementPtr = QSharedPointer<Element>; // QMap doesn't support move-only types using ElementPtr = QSharedPointer<Element>; // QMap doesn't support move-only types

View File

@@ -49,9 +49,9 @@ namespace BlackMiscTest
CValueCache cache(CValueCache::LocalOnly); CValueCache cache(CValueCache::LocalOnly);
QVERIFY(cache.getAllValues() == CVariantMap()); QVERIFY(cache.getAllValues() == CVariantMap());
cache.insertValues(testData); cache.insertValues({ testData, QDateTime::currentMSecsSinceEpoch() });
QVERIFY(cache.getAllValues() == testData); QVERIFY(cache.getAllValues() == testData);
cache.insertValues(testData2); cache.insertValues({ testData2, QDateTime::currentMSecsSinceEpoch() });
QVERIFY(cache.getAllValues() == testDataCombined); QVERIFY(cache.getAllValues() == testDataCombined);
} }
@@ -100,7 +100,7 @@ namespace BlackMiscTest
void CTestValueCache::localOnly() void CTestValueCache::localOnly()
{ {
CValueCache cache(CValueCache::LocalOnly); CValueCache cache(CValueCache::LocalOnly);
for (int i = 0; i < 4; ++i) { QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Uninitialized value")); } for (int i = 0; i < 4; ++i) { QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Uninitialized value")); }
CValueCacheUser user1(&cache); CValueCacheUser user1(&cache);
CValueCacheUser user2(&cache); CValueCacheUser user2(&cache);
testCommon(user1, user2); testCommon(user1, user2);
@@ -109,7 +109,7 @@ namespace BlackMiscTest
void CTestValueCache::localOnlyWithThreads() void CTestValueCache::localOnlyWithThreads()
{ {
CValueCache cache(CValueCache::LocalOnly); CValueCache cache(CValueCache::LocalOnly);
for (int i = 0; i < 4; ++i) { QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Uninitialized value")); } for (int i = 0; i < 4; ++i) { QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Uninitialized value")); }
CValueCacheUser user1(&cache); CValueCacheUser user1(&cache);
CValueCacheUser user2(&cache); CValueCacheUser user2(&cache);
CRegularThread thread; CRegularThread thread;
@@ -128,18 +128,18 @@ namespace BlackMiscTest
CValueCache thisCache(CValueCache::Distributed); CValueCache thisCache(CValueCache::Distributed);
CValueCache otherCache(CValueCache::Distributed); CValueCache otherCache(CValueCache::Distributed);
connect(&thisCache, &CValueCache::valuesChangedByLocal, &thisCache, [ & ](const CVariantMap &values) connect(&thisCache, &CValueCache::valuesChangedByLocal, &thisCache, [ & ](const CValueCachePacket &values)
{ {
QMetaObject::invokeMethod(&thisCache, "changeValuesFromRemote", Q_ARG(BlackMisc::CVariantMap, values), Q_ARG(BlackMisc::CIdentifier, thisProcess)); QMetaObject::invokeMethod(&thisCache, "changeValuesFromRemote", Q_ARG(BlackMisc::CValueCachePacket, values), Q_ARG(BlackMisc::CIdentifier, thisProcess));
QMetaObject::invokeMethod(&otherCache, "changeValuesFromRemote", Q_ARG(BlackMisc::CVariantMap, values), Q_ARG(BlackMisc::CIdentifier, otherProcess)); QMetaObject::invokeMethod(&otherCache, "changeValuesFromRemote", Q_ARG(BlackMisc::CValueCachePacket, values), Q_ARG(BlackMisc::CIdentifier, otherProcess));
}); });
connect(&otherCache, &CValueCache::valuesChangedByLocal, &thisCache, [ & ](const CVariantMap &values) connect(&otherCache, &CValueCache::valuesChangedByLocal, &thisCache, [ & ](const CValueCachePacket &values)
{ {
QMetaObject::invokeMethod(&thisCache, "changeValuesFromRemote", Q_ARG(BlackMisc::CVariantMap, values), Q_ARG(BlackMisc::CIdentifier, otherProcess)); QMetaObject::invokeMethod(&thisCache, "changeValuesFromRemote", Q_ARG(BlackMisc::CValueCachePacket, values), Q_ARG(BlackMisc::CIdentifier, otherProcess));
QMetaObject::invokeMethod(&otherCache, "changeValuesFromRemote", Q_ARG(BlackMisc::CVariantMap, values), Q_ARG(BlackMisc::CIdentifier, thisProcess)); QMetaObject::invokeMethod(&otherCache, "changeValuesFromRemote", Q_ARG(BlackMisc::CValueCachePacket, values), Q_ARG(BlackMisc::CIdentifier, thisProcess));
}); });
for (int i = 0; i < 4; ++i) { QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Uninitialized value")); } for (int i = 0; i < 4; ++i) { QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Uninitialized value")); }
CValueCacheUser thisUser(&thisCache); CValueCacheUser thisUser(&thisCache);
CValueCacheUser otherUser(&otherCache); CValueCacheUser otherUser(&otherCache);
@@ -160,7 +160,7 @@ namespace BlackMiscTest
void CTestValueCache::batched() void CTestValueCache::batched()
{ {
CValueCache cache(CValueCache::LocalOnly); CValueCache cache(CValueCache::LocalOnly);
for (int i = 0; i < 4; ++i) { QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Uninitialized value")); } for (int i = 0; i < 4; ++i) { QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Uninitialized value")); }
CValueCacheUser user1(&cache); CValueCacheUser user1(&cache);
CValueCacheUser user2(&cache); CValueCacheUser user2(&cache);
@@ -212,7 +212,7 @@ namespace BlackMiscTest
{ "namespace2/atcstations", CVariant::from(atcStations) } { "namespace2/atcstations", CVariant::from(atcStations) }
}; };
CValueCache cache(CValueCache::LocalOnly); CValueCache cache(CValueCache::LocalOnly);
cache.insertValues(testData); cache.insertValues({ testData, QDateTime::currentMSecsSinceEpoch() });
QDir dir(QDir::currentPath() + "/testcache"); QDir dir(QDir::currentPath() + "/testcache");
if (dir.exists()) { dir.removeRecursively(); } if (dir.exists()) { dir.removeRecursively(); }