0

Firstly I'd like to point out that I've looked this up but can't find the answer I'm looking for/have got confused with overly detailed answers.

I have a program which uses two threads. A Boolean values need to be set and read in Thread A but only read in Thread B.

Thread A:

Module::Module(){
}

void Module::foo(){
    mutex.lock();
    bool request = true;
    mutex.unlock();
}

void Module::bar(){
    mutex.lock();
    if (request){
        mutex.unlock();
        // do stuff
    }else{
        mutex.unlock();
    }
} 

Thread B:

Provider::Provider(){
    module = new Module;  // pointer to class request 'lives in'
}

void Provider::foo(){
    mutex.lock();
    if (module->request){
        mutex.unlock();
        // do stuff
        }
    }else{
        mutex.unlock();
    }
}

My question might seem rather trivial, but it's bugged me. Thread A cannot read and write at the same time, thus i'd argue recursive mutex is not required for A. However, there is a small possibility foo() and bar() could get called simultaneous from Thread B (Signals and slots). Does this mean I need recursive mutex?

Also; is there any reason not to use a Qt::BlockingQueudConnection? A colleague argued that this is dangerous as it sends calling threads to sleep until the signal has executed the slot- but is this not sort of the same as mutex?

Furthermore; seen a post regarding structuring mutex (pthread mutex locking variables used in statements). In here it mentions making local copies on values. If i was to employ something similar for Thread A e.g.

mutex.lock();
requestCopy = request;
mutex.lock();
...
if(requestCopy){
// do stuff
}

Will this also block access of request whereever requestCopy is getting used? I was looking to use this style in my code for simplicity but this would not work if you read AND write in a thread?

Any help would be great.

SamMetix
  • 1
  • 5
  • I don't see bar() anywhere called from thread B. – Olaf Dietsche Nov 10 '16 at 14:57
  • 1
    Perhaps off topic but... with regards exception safety you should prefer the [`RAII`](http://en.cppreference.com/w/cpp/language/raii) style of lock acquisition using e.g. [`lock_guard`](http://en.cppreference.com/w/cpp/thread/lock_guard) rather than explicit `lock`/`unlock` pairs. – G.M. Nov 10 '16 at 15:04
  • Thanks @G.M. A bit new to this so will look into it. Olaf Dietsche - sorry for simplicity I didn't show - however bar() will get called asysnc from time to time from Thread B. I've designed it so that Thread A & B shouldn't be calling foo() simultaneously - however I can't guarantee it. – SamMetix Nov 10 '16 at 15:24
  • @G.M. Sorry, also worth noting that i'm using Qt's QMutex class. Do you specifically recommend std::mutex? – SamMetix Nov 10 '16 at 15:38
  • 1
    Use `QMutexLocker`! And if you're really having a `bool` data type, then using the mutex is unnecessary. Use `QAtomicInteger` instead. If you don't use a boolean, but a more complex type, edit your question to indicate what type. – Kuba hasn't forgotten Monica Nov 10 '16 at 21:00
  • Hi @KubaOber. It is indeed just a boolean value I was looking to check against. Hadn't really seen atomic integers before but my understand is they are thread safe, i'm only confused as to how to declare it. I can just declare in my header file "public: QAtomicInteger request; " and now I can use request as if it's threadsafe? – SamMetix Nov 11 '16 at 10:41

1 Answers1

0

From what you have shown, it looks like (rewritten)

Some module (Thread A):

class Module {
private:
    bool request = false;
    QMutex m;
public:    
    void set_request(bool b) {
        QMutexLocker lock(&m);
        request = b;
    }

    bool get_request() {
        QMutexLocker lock(&m);
        return request;
    }

    void bar() {
        if (get_request()) {
            // do stuff
        }
    }
};

Thread B:

class Provider {
public:
    Provider() {
        module = new Module();
    }

    void foo() {
        if (module->get_request()){
            // do stuff
        }
    }
private:
    Module *module;
};

If this is really the case (and everything is fine this way), there is no need for a recursive mutex.

Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
  • Hi, thanks for the quick reply. I'm using QMutex (although maybe i should use std::mutex). Really sorry for the confusion : My threads are running two classes (in event loops). So Thread A is running Module and Thread B is running a class accessing a pointer to Module ( module->request). Also foo() is effectively a setter, but bar() is not a getter - it's checking the request variable. Module has two methods - foo() & bar(). Provider (Thread B) has foo(). – SamMetix Nov 10 '16 at 15:42
  • Looking at QMutex, using [`QMutexLocker`](http://doc.qt.io/qt-5/qmutexlocker.html) seems doing the same. – Olaf Dietsche Nov 10 '16 at 15:45
  • Ok, thanks for the swift replies- really appreciate it. – SamMetix Nov 10 '16 at 15:45
  • Even if you wrap the code in classes, this doesn't change the principal structure. Instead of manually locking and unlocking, do it in a get/set member function and call these. – Olaf Dietsche Nov 10 '16 at 16:02
  • Ok. So I have a setter and getter on Module. These methods will have QMutexLocker (with no unlock as mutex gets destroyed by wrapper). This means that I can call getter() from thread B normally using pointers? I.e. module->getRequest() and this will be safe? – SamMetix Nov 10 '16 at 16:12
  • QMutexLocker will lock the mutex, when it is created. It will unlock the mutex, when it goes out of scope, see http://doc.qt.io/qt-5/qmutexlocker.html for an explanation and examples. As I said before, std::mutex/std::lock_guard and QMutex/QMutexLocker seem to work the same. – Olaf Dietsche Nov 10 '16 at 16:17
  • If there are more shared resources, you must protect these as well, of course. If what you showed is the only part needing protection, you should be fine. – Olaf Dietsche Nov 10 '16 at 16:22
  • Sorry for the confusion. I was more asking about Provider calling Module's bar() method. In Qt signals and slots are used to call functions safely from different threads. If i have a getter on Module which is mutexed correctly, is it safe to call this from Provider? I.e. In Provider: module = new Module; module->foo(); – SamMetix Nov 10 '16 at 16:25
  • 1
    Also consider using `std::atomic`, which does exactly what that setter/getter does – Bruno Ferreira Nov 10 '16 at 16:39
  • module->foo() would be module->set_request(), right? You can call this from Provider without problems. – Olaf Dietsche Nov 10 '16 at 16:49
  • Yeah. Thanks alot for your time and effort. – SamMetix Nov 10 '16 at 17:16