Recursive mutex and QWaitCondition
-
I know these two things don't quite work together, but I kind of need them both for something... There's probably a way to work around it, but I'm pretty new with all things Qt so I thought you guys might be able to help me out.
I won't exactly show real code, but imagine all these methods are public in a class, and multiple threads can work on the same object.
If I understand how recursive mutexes work, you'd need one in that kind of situation below, right?
@class myclass
{
QMutex mutex;funcA()
{
mutex.lock();//... do stuff
mutex.unlock();
}funcB()
{
mutex.lock();//... do some stuff
funcA();
//... do some more stuffmutex.unlock();
}} //myclass@
My class has this kind of stuff, so I need a recursive mutex. Otherwise, I'll get deadlocks, or the mutex will unlocked way too early, causing potential unexpected behavior.
Now, in one of the methods, I want to be able to pause the thread and wait for another method to be called by another thread. Something like this:
@class myclass
{
QMutex mutex;
QQueue<QWaitCondition*> queue; //some kind of waiting queuefuncA2()
{
mutex.lock();//... do stuff
mutex.unlock();
}funcB2()
{
mutex.lock();//... do some stuff
//under some conditions, wait for funcC2 to be called by another thread
if (...)
{
QWaitCondition cond;
queue.append(&cond);cond.wait(&mutex, 10000); //if mutex is recursive, this will always fail
if (queue.removeOne(&cond))
{
//timer expired, noone called funcC2
}
else
{
//another thread called funcC2, which removed &cond from the queue already
}
}//... do some stuff
funcA2();
//... do some more stuffmutex.unlock();
}funcC2()
{
mutex.lock();if (!queue.isEmpty())
queue.takeFirst()->wakeOne();mutex.unlock();
}} //myclass@
That's more or less what I have at the moment... but it just can't work:
If my mutex is recursive, the QWaitCondition.wait(...) call returns immediatly. If my mutex is non-recursive, I get deadlocks, or I unlock my object too early.
I can use another dummy mutex for the .wait call, and unlock / relock my mutex manually before and after it, but I lose the atomic transition...
I could reorganize my code to only have a single pair of Lock/Unlock in each public method, and make sure none of the public methods are calling another, and make my mutex non-recursive, but this makes it very easy to introduce major bugs with minor code tweaks. Maybe that's the way to do it, I have no idea... I can't say I worked with mutexes that much, but I always tend to go with the "put the lock in every function that play with your data" kind of approach, and that includes private functions called by the (also lock'd) public ones...Maybe my design is severely flawed, or maybe I'm missing something obvious I could use. Either way, thanks in advance for any suggestions
-
The problem you describe is the defined behavior of "QWaitCondition::wait":http://doc.qt.nokia.com/latest/qwaitcondition.html#wait
bq. Releases the locked mutex and waits on the wait condition. The mutex must be initially locked by the calling thread. If mutex is not in a locked state, this function returns immediately. If mutex is a recursive mutex, this function returns immediately. ...
-
I know... what I'm asking is: Because of this, how can I accomplish what I'm trying to do? As I said, it's probably more of a design issue and I guess I need to find a different way to signal the other thread. But I was wondering if you guys had an idea of something that could accomplish something similar to QWaitCondition, but that could work in this case, where I need a recursive mutex.
-
what you could do is have interface methods and internal methods. do the locking on the interface methods level and only call internals inside.
@
class myclass
{
public:
funcA2()
{
mutex.lock();
funA2int();
mutex.unlock();
}
funcB2()
{
mutex.lock();
funB2int();
mutex.unlock();
}
funcC2()
{
mutex.lock();
funC2int();
mutex.unlock();
}private:
funA2int()
{
//... do stuff
}funB2int() { //... do some stuff //under some conditions, wait for funcC2 to be called by another thread if (...) { QWaitCondition cond; queue.append(&cond); cond.wait(&mutex, 10000); //if mutex is recursive, this will always fail if (queue.removeOne(&cond)) { //timer expired, noone called funcC2 } else { //another thread called funcC2, which removed &cond from the queue already } } //... do some stuff funcA2int(); //... do some more stuff } QMutex mutex; QQueue<QWaitCondition*> queue; //some kind of waiting queue funcC2int() { if (!queue.isEmpty()) queue.takeFirst()->wakeOne(); }
} //myclass
@ -
That would work, I guess... It can get pretty messy though.
But I think I can make it work using a second non-recursive mutex that is only used to inter-lock my two methods, while the "global" recursive one can still be used everywhere safely... as long as one specific method (illustrated by funcB2 in the example) is not called by another locked method, which should work in my case.
Thanks again; I just wanted to be sure I wasn't missing an obvious solution or something.