#4182: Util: Make load()/save()/clear() operations on configurations thread-safe

This commit is contained in:
Günter Obiltschnig 2023-10-12 10:38:04 +02:00
parent cb58e09304
commit 33d5d9c083
8 changed files with 73 additions and 10 deletions

View File

@ -421,6 +421,37 @@ public:
/// Returns true iff events are enabled. /// Returns true iff events are enabled.
protected: protected:
class ScopedLock
/// A helper class allowing to temporarily
/// lock an entire AbstractConfiguration,
/// for use by subclasses. A typical use
/// case is loading or saving an entire
/// configuration in a thread-safe way.
///
/// Caution: Thoughtless use of this class
/// may easily lead to deadlock situations
/// in connection with events if any of the
/// mutating methods (set...(), remove())
/// are called with the lock held. Therefore
/// this class is available to subclasses
/// only, not for general use.
{
public:
explicit ScopedLock(const AbstractConfiguration& c):
_c(c)
{
_c._mutex.lock();
}
~ScopedLock()
{
_c._mutex.unlock();
}
private:
const AbstractConfiguration& _c;
};
virtual bool getRaw(const std::string& key, std::string& value) const = 0; virtual bool getRaw(const std::string& key, std::string& value) const = 0;
/// If the property with the given key exists, stores the property's value /// If the property with the given key exists, stores the property's value
/// in value and returns true. Otherwise, returns false. /// in value and returns true. Otherwise, returns false.
@ -493,6 +524,7 @@ private:
friend class ConfigurationView; friend class ConfigurationView;
friend class LocalConfigurationView; friend class LocalConfigurationView;
friend class ConfigurationMapper; friend class ConfigurationMapper;
friend class ScopedLock;
}; };

View File

@ -105,7 +105,6 @@ std::string AbstractConfiguration::getRawString(const std::string& key) const
std::string AbstractConfiguration::getRawString(const std::string& key, const std::string& defaultValue) const std::string AbstractConfiguration::getRawString(const std::string& key, const std::string& defaultValue) const
{ {
Mutex::ScopedLock lock(_mutex); Mutex::ScopedLock lock(_mutex);
std::string value; std::string value;

View File

@ -59,6 +59,8 @@ IniFileConfiguration::~IniFileConfiguration()
void IniFileConfiguration::load(std::istream& istr) void IniFileConfiguration::load(std::istream& istr)
{ {
AbstractConfiguration::ScopedLock lock(*this);
_map.clear(); _map.clear();
_sectionKey.clear(); _sectionKey.clear();
while (!istr.eof()) while (!istr.eof())

View File

@ -67,6 +67,8 @@ void JSONConfiguration::load(const std::string& path)
void JSONConfiguration::load(std::istream& istr) void JSONConfiguration::load(std::istream& istr)
{ {
AbstractConfiguration::ScopedLock lock(*this);
JSON::Parser parser; JSON::Parser parser;
parser.parse(istr); parser.parse(istr);
DynamicAny result = parser.result(); DynamicAny result = parser.result();
@ -79,6 +81,8 @@ void JSONConfiguration::load(std::istream& istr)
void JSONConfiguration::loadEmpty(const std::string& root) void JSONConfiguration::loadEmpty(const std::string& root)
{ {
AbstractConfiguration::ScopedLock lock(*this);
_object = new JSON::Object(); _object = new JSON::Object();
JSON::Object::Ptr rootObject = new JSON::Object(); JSON::Object::Ptr rootObject = new JSON::Object();
_object->set(root, rootObject); _object->set(root, rootObject);
@ -106,7 +110,7 @@ void JSONConfiguration::getIndexes(std::string& name, std::vector<int>& indexes)
int firstOffset = -1; int firstOffset = -1;
int offset = 0; int offset = 0;
RegularExpression regex("\\[([0-9]+)\\]"); RegularExpression regex("\\[([0-9]+)\\]");
while(regex.match(name, offset, matches) > 0) while (regex.match(name, offset, matches) > 0)
{ {
if (firstOffset == -1) if (firstOffset == -1)
{ {
@ -131,7 +135,7 @@ JSON::Object::Ptr JSONConfiguration::findStart(const std::string& key, std::stri
StringTokenizer tokenizer(key, "."); StringTokenizer tokenizer(key, ".");
lastPart = tokenizer[tokenizer.count() - 1]; lastPart = tokenizer[tokenizer.count() - 1];
for(int i = 0; i < tokenizer.count() - 1; ++i) for (int i = 0; i < tokenizer.count() - 1; ++i)
{ {
std::vector<int> indexes; std::vector<int> indexes;
std::string name = tokenizer[i]; std::string name = tokenizer[i];
@ -241,7 +245,6 @@ JSON::Object::Ptr JSONConfiguration::findStart(const std::string& key, std::stri
void JSONConfiguration::setValue(const std::string& key, const Poco::DynamicAny& value) void JSONConfiguration::setValue(const std::string& key, const Poco::DynamicAny& value)
{ {
std::string sValue; std::string sValue;
value.convert<std::string>(sValue); value.convert<std::string>(sValue);
@ -276,12 +279,12 @@ void JSONConfiguration::setValue(const std::string& key, const Poco::DynamicAny&
} }
JSON::Array::Ptr arr = result.extract<JSON::Array::Ptr>(); JSON::Array::Ptr arr = result.extract<JSON::Array::Ptr>();
for(std::vector<int>::iterator it = indexes.begin(); it != indexes.end() - 1; ++it) for (std::vector<int>::iterator it = indexes.begin(); it != indexes.end() - 1; ++it)
{ {
JSON::Array::Ptr nextArray = arr->getArray(*it); JSON::Array::Ptr nextArray = arr->getArray(*it);
if (nextArray.isNull()) if (nextArray.isNull())
{ {
for(int i = static_cast<int>(arr->size()); i <= *it; ++i) for (int i = static_cast<int>(arr->size()); i <= *it; ++i)
{ {
Poco::DynamicAny nullValue; Poco::DynamicAny nullValue;
arr->add(nullValue); arr->add(nullValue);
@ -345,14 +348,14 @@ void JSONConfiguration::enumerate(const std::string& key, Keys& range) const
void JSONConfiguration::save(std::ostream& ostr, unsigned int indent) const void JSONConfiguration::save(std::ostream& ostr, unsigned int indent) const
{ {
AbstractConfiguration::ScopedLock lock(*this);
_object->stringify(ostr, indent); _object->stringify(ostr, indent);
} }
void JSONConfiguration::removeRaw(const std::string& key) void JSONConfiguration::removeRaw(const std::string& key)
{ {
std::string lastPart; std::string lastPart;
JSON::Object::Ptr parentObject = findStart(key, lastPart); JSON::Object::Ptr parentObject = findStart(key, lastPart);
std::vector<int> indexes; std::vector<int> indexes;
@ -367,7 +370,6 @@ void JSONConfiguration::removeRaw(const std::string& key)
DynamicAny result = parentObject->get(lastPart); DynamicAny result = parentObject->get(lastPart);
if (!result.isEmpty() && result.type() == typeid(JSON::Array::Ptr)) if (!result.isEmpty() && result.type() == typeid(JSON::Array::Ptr))
{ {
JSON::Array::Ptr arr = result.extract<JSON::Array::Ptr>(); JSON::Array::Ptr arr = result.extract<JSON::Array::Ptr>();
for(std::vector<int>::iterator it = indexes.begin(); it != indexes.end() - 1; ++it) for(std::vector<int>::iterator it = indexes.begin(); it != indexes.end() - 1; ++it)
{ {
@ -376,7 +378,6 @@ void JSONConfiguration::removeRaw(const std::string& key)
arr->remove(indexes.back()); arr->remove(indexes.back());
} }
} }
} }

View File

@ -73,6 +73,8 @@ void LayeredConfiguration::add(AbstractConfiguration::Ptr pConfig, int priority,
void LayeredConfiguration::add(AbstractConfiguration::Ptr pConfig, const std::string& label, int priority, bool writeable) void LayeredConfiguration::add(AbstractConfiguration::Ptr pConfig, const std::string& label, int priority, bool writeable)
{ {
AbstractConfiguration::ScopedLock lock(*this);
ConfigItem item; ConfigItem item;
item.pConfig = pConfig; item.pConfig = pConfig;
item.priority = priority; item.priority = priority;
@ -87,6 +89,8 @@ void LayeredConfiguration::add(AbstractConfiguration::Ptr pConfig, const std::st
void LayeredConfiguration::removeConfiguration(AbstractConfiguration::Ptr pConfig) void LayeredConfiguration::removeConfiguration(AbstractConfiguration::Ptr pConfig)
{ {
AbstractConfiguration::ScopedLock lock(*this);
for (ConfigList::iterator it = _configs.begin(); it != _configs.end(); ++it) for (ConfigList::iterator it = _configs.begin(); it != _configs.end(); ++it)
{ {
if (it->pConfig == pConfig) if (it->pConfig == pConfig)
@ -100,6 +104,8 @@ void LayeredConfiguration::removeConfiguration(AbstractConfiguration::Ptr pConfi
AbstractConfiguration::Ptr LayeredConfiguration::find(const std::string& label) const AbstractConfiguration::Ptr LayeredConfiguration::find(const std::string& label) const
{ {
AbstractConfiguration::ScopedLock lock(*this);
for (const auto& conf: _configs) for (const auto& conf: _configs)
{ {
if (conf.label == label) return conf.pConfig; if (conf.label == label) return conf.pConfig;

View File

@ -32,6 +32,8 @@ MapConfiguration::~MapConfiguration()
void MapConfiguration::copyTo(AbstractConfiguration& config) void MapConfiguration::copyTo(AbstractConfiguration& config)
{ {
AbstractConfiguration::ScopedLock lock(*this);
for (const auto& p: _map) for (const auto& p: _map)
{ {
config.setString(p.first, p.second); config.setString(p.first, p.second);
@ -41,6 +43,8 @@ void MapConfiguration::copyTo(AbstractConfiguration& config)
void MapConfiguration::clear() void MapConfiguration::clear()
{ {
AbstractConfiguration::ScopedLock lock(*this);
_map.clear(); _map.clear();
} }

View File

@ -53,6 +53,8 @@ PropertyFileConfiguration::~PropertyFileConfiguration()
void PropertyFileConfiguration::load(std::istream& istr) void PropertyFileConfiguration::load(std::istream& istr)
{ {
AbstractConfiguration::ScopedLock lock(*this);
clear(); clear();
while (!istr.eof()) while (!istr.eof())
{ {
@ -73,6 +75,8 @@ void PropertyFileConfiguration::load(const std::string& path)
void PropertyFileConfiguration::save(std::ostream& ostr) const void PropertyFileConfiguration::save(std::ostream& ostr) const
{ {
AbstractConfiguration::ScopedLock lock(*this);
MapConfiguration::iterator it = begin(); MapConfiguration::iterator it = begin();
MapConfiguration::iterator ed = end(); MapConfiguration::iterator ed = end();
while (it != ed) while (it != ed)

View File

@ -159,6 +159,8 @@ void XMLConfiguration::load(const Poco::XML::Document* pDocument)
{ {
poco_check_ptr (pDocument); poco_check_ptr (pDocument);
AbstractConfiguration::ScopedLock lock(*this);
_pDocument = Poco::XML::AutoPtr<Poco::XML::Document>(const_cast<Poco::XML::Document*>(pDocument), true); _pDocument = Poco::XML::AutoPtr<Poco::XML::Document>(const_cast<Poco::XML::Document*>(pDocument), true);
_pRoot = Poco::XML::AutoPtr<Poco::XML::Node>(pDocument->documentElement(), true); _pRoot = Poco::XML::AutoPtr<Poco::XML::Node>(pDocument->documentElement(), true);
} }
@ -174,6 +176,8 @@ void XMLConfiguration::load(const Poco::XML::Node* pNode)
} }
else else
{ {
AbstractConfiguration::ScopedLock lock(*this);
_pDocument = Poco::XML::AutoPtr<Poco::XML::Document>(pNode->ownerDocument(), true); _pDocument = Poco::XML::AutoPtr<Poco::XML::Document>(pNode->ownerDocument(), true);
_pRoot = Poco::XML::AutoPtr<Poco::XML::Node>(const_cast<Poco::XML::Node*>(pNode), true); _pRoot = Poco::XML::AutoPtr<Poco::XML::Node>(const_cast<Poco::XML::Node*>(pNode), true);
} }
@ -182,6 +186,8 @@ void XMLConfiguration::load(const Poco::XML::Node* pNode)
void XMLConfiguration::loadEmpty(const std::string& rootElementName) void XMLConfiguration::loadEmpty(const std::string& rootElementName)
{ {
AbstractConfiguration::ScopedLock lock(*this);
_pDocument = new Poco::XML::Document; _pDocument = new Poco::XML::Document;
_pRoot = _pDocument->createElement(rootElementName); _pRoot = _pDocument->createElement(rootElementName);
_pDocument->appendChild(_pRoot); _pDocument->appendChild(_pRoot);
@ -190,6 +196,8 @@ void XMLConfiguration::loadEmpty(const std::string& rootElementName)
void XMLConfiguration::save(const std::string& path) const void XMLConfiguration::save(const std::string& path) const
{ {
AbstractConfiguration::ScopedLock lock(*this);
Poco::XML::DOMWriter writer; Poco::XML::DOMWriter writer;
writer.setNewLine("\n"); writer.setNewLine("\n");
writer.setOptions(Poco::XML::XMLWriter::PRETTY_PRINT); writer.setOptions(Poco::XML::XMLWriter::PRETTY_PRINT);
@ -199,6 +207,8 @@ void XMLConfiguration::save(const std::string& path) const
void XMLConfiguration::save(std::ostream& ostr) const void XMLConfiguration::save(std::ostream& ostr) const
{ {
AbstractConfiguration::ScopedLock lock(*this);
Poco::XML::DOMWriter writer; Poco::XML::DOMWriter writer;
writer.setNewLine("\n"); writer.setNewLine("\n");
writer.setOptions(Poco::XML::XMLWriter::PRETTY_PRINT); writer.setOptions(Poco::XML::XMLWriter::PRETTY_PRINT);
@ -208,12 +218,16 @@ void XMLConfiguration::save(std::ostream& ostr) const
void XMLConfiguration::save(Poco::XML::DOMWriter& writer, const std::string& path) const void XMLConfiguration::save(Poco::XML::DOMWriter& writer, const std::string& path) const
{ {
AbstractConfiguration::ScopedLock lock(*this);
writer.writeNode(path, _pDocument); writer.writeNode(path, _pDocument);
} }
void XMLConfiguration::save(Poco::XML::DOMWriter& writer, std::ostream& ostr) const void XMLConfiguration::save(Poco::XML::DOMWriter& writer, std::ostream& ostr) const
{ {
AbstractConfiguration::ScopedLock lock(*this);
writer.writeNode(ostr, _pDocument); writer.writeNode(ostr, _pDocument);
} }
@ -476,4 +490,5 @@ Poco::XML::Node* XMLConfiguration::findAttribute(const std::string& name, Poco::
} } // namespace Poco::Util } } // namespace Poco::Util
#endif // POCO_UTIL_NO_XMLCONFIGURATION #endif // POCO_UTIL_NO_XMLCONFIGURATION