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);
});
connect(CSettingsCache::instance(), &CSettingsCache::valuesChangedByLocal, [this](const CVariantMap &settings)
connect(CSettingsCache::instance(), &CSettingsCache::valuesChangedByLocal, [this](const CValueCachePacket &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
CSettingsCache::instance()->changeValuesFromRemote(settings, origin);
@@ -120,7 +120,7 @@ namespace BlackCore
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(origin);

View File

@@ -95,7 +95,7 @@ namespace BlackCore
//! One or more settings were changed
//! \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
//! \note Used to register hotkey action, do not use directly
@@ -138,10 +138,10 @@ namespace BlackCore
//! Ratify some settings changed by another process
//! \note Not pure because it can be called from the base class constructor.
//! \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
virtual BlackMisc::CVariantMap getAllSettings() const = 0;
virtual BlackMisc::CValueCachePacket getAllSettings() const = 0;
//! Update local settings with settings from core
virtual void synchronizeLocalSettings() = 0;

View File

@@ -66,16 +66,16 @@ namespace BlackCore
// 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
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; }
return CSettingsCache::instance()->getAllValues();
return CSettingsCache::instance()->getAllValuesWithTimestamps();
}
void CContextApplication::synchronizeLocalSettings()

View File

@@ -45,10 +45,10 @@ namespace BlackCore
virtual void synchronizeLogSubscriptions();
//! \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
virtual BlackMisc::CVariantMap getAllSettings() const override;
virtual BlackMisc::CValueCachePacket getAllSettings() const override;
//! \copydoc IContextApplication::synchronizeLocalSettings
virtual void synchronizeLocalSettings() override;

View File

@@ -50,7 +50,7 @@ namespace BlackCore
"logSubscriptionRemoved", this, SIGNAL(logSubscriptionRemoved(BlackMisc::CIdentifier, BlackMisc::CLogPattern)));
Q_ASSERT(s);
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);
s = connection.connect(serviceName, IContextApplication::ObjectPath(), IContextApplication::InterfaceName(),
"registrationChanged", this, SIGNAL(registrationChanged()));
@@ -93,14 +93,14 @@ namespace BlackCore
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);
}
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()

View File

@@ -41,10 +41,10 @@ namespace BlackCore
virtual void synchronizeLogSubscriptions();
//! \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
virtual BlackMisc::CVariantMap getAllSettings() const override;
virtual BlackMisc::CValueCachePacket getAllSettings() const override;
//! \copydoc IContextApplication::synchronizeLocalSettings
virtual void synchronizeLocalSettings() override;

View File

@@ -26,7 +26,7 @@ namespace BlackCore
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(); });
if (! QFile::exists(m_revisionFileName)) { QFile(m_revisionFileName).open(QFile::WriteOnly); }
@@ -116,7 +116,7 @@ namespace BlackCore
if (m_revision != newRevision)
{
m_revision = newRevision;
CVariantMap newValues;
CValueCachePacket newValues;
loadFromFiles(persistentStore(), newValues);
m_deferredChanges.insert(newValues);
}

View File

@@ -49,7 +49,7 @@ namespace BlackCore
QFileSystemWatcher m_watcher;
QUuid m_revision;
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; }
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)
{
// 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());
});
@@ -84,6 +84,7 @@ namespace BlackMisc
const QString m_key;
CVariant m_value;
int m_pendingChanges = 0;
std::atomic<qint64> m_timestamp { QDateTime::currentMSecsSinceEpoch() };
};
CValueCache::Element &CValueCache::getElement(const QString &key)
@@ -100,10 +101,11 @@ namespace BlackMisc
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);
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
@@ -117,13 +119,24 @@ namespace BlackMisc
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);
changeValues(values);
}
void CValueCache::changeValues(const CVariantMap &values)
void CValueCache::changeValues(const CValueCachePacket &values)
{
QMutexLocker lock(&m_mutex);
if (values.empty()) { return; }
@@ -137,16 +150,17 @@ namespace BlackMisc
Q_ASSERT(isSafeToIncrement(element.m_pendingChanges));
element.m_pendingChanges++;
element.m_value = in.value();
element.m_timestamp = in.timestamp();
}
emit valuesChanged(values, sender());
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);
if (values.empty()) { return; }
CVariantMap ratifiedChanges;
CValueCachePacket ratifiedChanges;
auto out = m_elements.lowerBound(values.cbegin().key());
auto end = m_elements.upperBound((values.cend() - 1).key());
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
{
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())
@@ -180,7 +195,7 @@ namespace BlackMisc
{
CVariantMap map;
map.convertFromJson(json);
insertValues(map);
insertValues({ map, QDateTime::currentMSecsSinceEpoch() });
}
CStatusMessage CValueCache::saveToFiles(const QString &dir, const QString &keyPrefix) const
@@ -229,13 +244,13 @@ namespace BlackMisc
CStatusMessage CValueCache::loadFromFiles(const QString &dir)
{
QMutexLocker lock(&m_mutex);
CVariantMap values;
CValueCachePacket values;
auto status = loadFromFiles(dir, values);
insertValues(values);
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);
if (! QDir(dir).isReadable())
@@ -258,7 +273,7 @@ namespace BlackMisc
CVariantMap temp;
temp.convertFromJson(json.object());
temp.removeDuplicates(currentValues);
o_values.insert(temp);
o_values.insert(temp, QFileInfo(file).lastModified().toMSecsSinceEpoch());
}
return {};
}
@@ -309,6 +324,7 @@ namespace BlackMisc
{}
const QString m_key;
LockFree<CVariant> m_value;
std::atomic<qint64> m_timestamp { QDateTime::currentMSecsSinceEpoch() };
const int m_metaType = QMetaType::UnknownType;
const Validator m_validator;
const CVariant m_default;
@@ -323,7 +339,7 @@ namespace BlackMisc
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)));
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());
if (! error.isEmpty())
@@ -358,7 +374,7 @@ namespace BlackMisc
element.m_pendingChanges++;
element.m_value.uniqueWrite() = value;
emit valuesWantToCache({ { element.m_key, value } });
emit valuesWantToCache({ { { element.m_key, value } }, QDateTime::currentMSecsSinceEpoch() });
}
}
else
@@ -369,13 +385,18 @@ namespace BlackMisc
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());
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
{
@@ -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
{
auto error = validate(*element, value);
auto error = validate(*element, it.value());
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); }
}
else
@@ -426,13 +448,15 @@ namespace BlackMisc
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));
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
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.
//! Values already in the cache will remain in the cache unless they are overwritten.
//! \threadsafe
void insertValues(const BlackMisc::CVariantMap &values);
void insertValues(const BlackMisc::CValueCachePacket &values);
//! Save values in Json format.
//! If prefix is provided then only those values whose keys start with that prefix.
@@ -131,20 +135,20 @@ namespace BlackMisc
//! \see BlackMisc::CValueCache::valuesChangedByLocal.
//! \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.
void changeValuesFromRemote(const BlackMisc::CVariantMap &values, const BlackMisc::CIdentifier &originator);
void changeValuesFromRemote(const BlackMisc::CValueCachePacket &values, const BlackMisc::CIdentifier &originator);
signals:
//! 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
//! 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:
//! Save specific values to Json files in a given directory.
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.
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.
mutable QMutex m_mutex { QMutex::Recursive };
@@ -158,14 +162,14 @@ namespace BlackMisc
Element &getElement(const QString &key);
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:
//! \private
void valuesChanged(const BlackMisc::CVariantMap &values, QObject *changedBy);
void valuesChanged(const BlackMisc::CValueCachePacket &values, QObject *changedBy);
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.
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.
CCached(const CCached &) = delete;

View File

@@ -22,6 +22,7 @@ namespace BlackMisc
{
class CIdentifier;
class CValueCache;
class CValueCachePacket;
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.
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.
//! Connected to signal CValueCache::valuesChanged.
//! \param values The new values.
//! \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.
void beginBatch();
@@ -81,7 +85,7 @@ namespace BlackMisc
signals:
//! Synchronize this page's changes to other pages.
//! Connected to slot CValueCache::changeValues.
void valuesWantToCache(const BlackMisc::CVariantMap &values);
void valuesWantToCache(const BlackMisc::CValueCachePacket &values);
private:
using ElementPtr = QSharedPointer<Element>; // QMap doesn't support move-only types