My aim is to make a QString threadsafe (by default, a QString is reentrant).
I want to use a map with multiple QStrings and access them with multiple threads. Most of the time, the threads will read but sometimes 1+ of them will write data to these strings. It should work like a kind of "database". When multiple threads are writing at the same time, it doesn't matter which thread will "win".
I can lock every access to the QStrings or make an wrapper object around it. My idea is to make an Inheritance from the QString and use a QReadWriteLock instance in the assignment Operator. So every time I use an instance of this (tsQString) I can be sure that it's threadsafe and need no locking from "outside"
With this approach, I can't lock the constructors of the derivate class. This is not possible:
explicit tsQString(QString str) {
rhs._lo.lockForRead();
_lo.lockForWrite();
QString::operator =(rhs);
_lo.unlock();
rhs._lo.unlock();
return *this;
}
But is it really necessary to lock the constructors or is locking of the assignment operators enough for the thread safety?
In my setup I'll use the implicit sharing concept and later on I’ll try to make a QMap thread safe with this approach.
class tsQString final : public QString
{
public:
explicit tsQString() : tsQString(QString()){}
explicit tsQString(QString str) : QString(str){}
~tsQString() = default;
tsQString (tsQString &&rhs) : QString(rhs){}
tsQString (const tsQString &rhs) : QString(rhs){}
tsQString &operator = (const tsQString &rhs)
{
rhs._lo.lockForRead();
_lo.lockForWrite();
QString::operator =(rhs);
_lo.unlock();
rhs._lo.unlock();
return *this;
}
tsQString &operator = (tsQString &&rhs)
{
rhs._lo.lockForRead();
_lo.lockForWrite();
QString::operator =(rhs);
_lo.unlock();
rhs._lo.unlock();
return *this;
}
private:
mutable QReadWriteLock _lo {};
};
edit : It will be better using std lib for synchronization because of std::lock
std::unique_lock<std::mutex> l1(_muLo, std::defer_lock);
std::unique_lock<std::mutex> l2(rhs._muLo, std::defer_lock);
std::lock(l1, l2); //lock using a deadlock avoidance algorithm
private:
mutable std::mutex _muLo{};