stopThread() & MessageManagerLock -> deadlock


#1

I made a custom FileTreeComponentEx which has a buildDirectory(String dir) function. This function, when called from the UI Thread, will create a new Thread that fills up the TreeView with the contents of a directory.

In this thread I have to use MessageManagerLocks every time I add a new TreeViewItem and set its text (because this will initiate a repaint() I guess).

The problem is that when I call stopThread(10000) from the UI Thread to stop my Thread that is filling the TreeView with the directory contents, I get a deadlock. It seems that the problem is the use of the MessageManagerLock in that particular thread.

In the JUCE help about MessageManagerLock it is stated:

“Another caveat is that using this in conjunction with other CriticalSections can create lots of interesting ways of producing a deadlock! In particular, if your message thread calls stopThread() for a thread that uses these locks, you’ll get an (occasional) deadlock…”

Why is that and what can I do about it?
Why does the stopThread() function fail when the Message Manager is locked (or vice versa) ?


#2

Extra question concerning the lifetime of the MessageManagerLock object:

If I write

void myFunction()
{
{
const MessageManagerLock mmLock1;

 ... code ...

}

{
const MessageManagerLock mmLock2;

 ... code ...

}
}

Are the individual MessageManagerLock objects destroyed after the brackets they are in are closed? I.e. are these 2 MessageManagerLock objects created & destroyed like calling:

MessageManagerLock *mmLock1=new MessageManagerLock();
delete mmLock1;
MessageManagerLock *mmLock2=new MessageManagerLock();
delete mmLock2;

or is this rather equal to calling:

MessageManagerLock *mmLock1=new MessageManagerLock();
MessageManagerLock *mmLock2=new MessageManagerLock();
delete mmLock1;
delete mmLock2;

Because if not, then this may well be the cause of the problem.


#3

Well, why don’t you put a break point into the destructor of the MessageManagerLock code, and then run through your function step-by-step and see where things created and destroyed?


#4

Just viewed the assembly and the brackets “trick” is ok.

So my initial question still lasts: Why does creating a MessageManagerLock object in a Thread induce a deadlock when stopThread() was called for this Thread?


#5

It’s pretty obvious if you think it through. The message thread always has the MM lock, so when you call stopThread on it, the MM is already locked for that thread. stopThread then waits for the other thread to stop, but if in the meantime, that other thread rolls along and hits a MM lock, each thread is then waiting for the other one. Deadlock.


#6

Ok. I am pretty happy to at least understand what’s going on. I will try to find some solution.


#7

Best just to avoid MM locks, or to let the threads stop without waiting for them.


#8

I just use signalThreadShouldExit() now and then run a Timer that just waits (non blocking, just by checking isThreadRunning() ) maximum X milliseconds for the the Thread being stopped and if it was not, just calls stopThread(0) and then jumps to a callback.

Would it be possible to add a TimeredStopThread class to JUCE? This would inherit from Thread and Timer and there would also be a class called TimeredStopThreadListener (this is just a class with some pure virtual function timerStopThreadStoppedCallback(TimeredStopThread *sender)).

TimeredStopThread::addListener() would add some listeners to the TimerStopThread that get called when it has stopped after calling timeredStop() - (see below).

TimeredStopThread::timeredStop(int milliseconds) would start the Timer and after X milliseconds just call all listeners’ timerStopThreadStoppedCallback() functions.

So all blocking issues gone for the MessageManagerLock in the Threads.


#9

Isn’t it easier to make your thread send a message just before it finishes?


#10

Yes, it is. That would save a timer.


#11