0

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{}; 
Cherubim
  • 13
  • 4
  • 1
    Do not inherit from `QString`! This will guide you strait to other problems. Also why you want to make `QString` thread safe? This is typical [XY problem](http://xyproblem.info/). – Marek R Sep 09 '19 at 11:46
  • I'm voting to close this question as off-topic because this is clearly [XY problem](http://xyproblem.info/). Please describe your actual problem not how to fix your solution of the problem. – Marek R Sep 09 '19 at 11:48
  • FYI implicit sharing is implemented by Qt in multiple places. `QString` and `QMap` are one of them. – Marek R Sep 09 '19 at 11:51
  • 1
    Locking constructors is pointless. You can't share object before it is constructed, ergo there is nothing to synchronize. – Marek R Sep 09 '19 at 11:52
  • Thanks for your answers! Why is an inheritance form QString a bad idea? I've edited my question and tried to explain my problem / the reason why I need a thread safe solution. Hope this solve the XY problem. – Cherubim Sep 09 '19 at 12:21
  • Do you understand what is not threadsafe about QString? It is safe to use QString instances in multiple threads. It is not threadsafe to modify the same instance from multiple threads. So as long as you are not using references but instances, you are fairly safe. – Thomas Sep 09 '19 at 12:37
  • Hello Thomas, thx for your input! I know, but i need to modify the same QString from multiple threads. There will be lots of QStrings, therefore my idea was to R/W look the String class itself. Every time i use this class, I will be threadsafe. – Cherubim Sep 10 '19 at 05:13

0 Answers0