Understanding ThreadWithProgressWindow


#1
#if JUCE_MODAL_LOOPS_PERMITTED
bool ThreadWithProgressWindow::runThread (const int priority)
{
    jassert (MessageManager::getInstance()->isThisTheMessageThread());

    startThread (priority);

I wonder what is the reasoning behind the above jassert. While roaming through the juce code I got used to descriptive messages describing assertions that followed.


#2

Yeh, that one’s pretty self descriptive though. It means that method should only be called from the message thread.


#3

I agree with you on that, but I am trying to understand why this is necessary.


#4

short answer:
Because it is a UI element. All UI related stuff must be done on the messageManager thread. Windows, in particular, will get all screwed up if you try and do UI-related things on other threads.


#5

Whouldn’t this be also achieved using a MessageManagerLock? (Or whould it lead to a bunch of obsure deadlocks?)

Edit: To make my intentions clear; Can I avoid bad karma by using a MessageManagerLock and ignore the assertion? I also seek for the long answer.


#6

Avoid locking if possible, one thing i learned here. Try to prepare your data in other threads, and inform the UI about the change so it updates itself. Don’t do any work in the UI thread itself. Thats the basic rule behing Audio Plugins and other software today. I use AsyncUpdater class to achieve this, hasn’t failed me yet.


#7

Short answer: No.

Long answer: In the case of that method, it runs a modal loop, which can only be done (and would only make sense) on the message thread.

Longer answer: Don’t use runThread() at all! Likewise you should avoid any code that you see inside a JUCE_MODAL_LOOPS_PERMITTED block. Modal loops are Bad News at the best of times, and aren’t even possible on newer OSes like android. I’ve left them in the code for backwards-compatibility, but when writing new code you should avoid them completely, and I’d actually recommend setting JUCE_MODAL_LOOPS_PERMITTED=0 to make certain of that.

(Interestingly, looking at ThreadWithProgressWindow, it looks like I need to add some non-modal alternative methods to it and update its comments to explain all this - I’ll do that now…)


#8

This post came up from code similar to the following. I have a FileDragAndDropTarget, which upon dropping a bunch of files, calls ThreadWithProgressWindow to do some time consuming stuff.

void MainComponent::filesDropped (const StringArray &files, int x, int y)
{
    // do sth cpu intensive
    TestThreadWithProgressWindow tpw;
    tpw.runThread();
}
void TestThreadWithProgressWindow::run()
{
    const int thingsToDo = 100;
    for (int i = 0; i < thingsToDo; ++i)
    {
        if (threadShouldExit())
            break;
        // this will update the progress bar on the dialog box
        setProgress (i / (double) thingsToDo);
        //   ... do the business here...
        sleep(200);
    }
}

Now, I understand that the above solution has a lot of problems (even when not taking into account the above comments). On Mac, Finder will just freeze -no rainbow wheels though- (which I believe is intended, since dropping a new bunch of files to the app, would try to call filesDropped, which hasn’t yet returned from the previous call).

Now the question becomes, How would you go with it?

Any help will be appreciated.

(Note: Using a CallbackMessage to start the ThreadWithProgressWindow later on doesn’t have any effect.)


#9

We had a similar problem with some flakey plugins recently and got around it by adding the StringArray to a list of pending files, then starting a timer for about 100ms. When the timer calls back stop it and process the list. Bit of a hack but worked.


#10

Was just having a look at this, and wondering what the equivalent would be now if avoiding modal loops, but I can't seem to find anything. Do I presume that you never actually got around to writing one, or is it just something that has a different name now?


#11

Bump! :)


#12

Good question - I guess all that's needed would be some tweaks to ThreadWithProgressWindow so that it can become modal but then return, and which automatically deletes itself + makes a callback when the thread is done. It's something I've been meaning to do, so thanks for the bump..


#13

Longer answer: Don't use runThread() at all!

I'd suggest that this be deprecated in the documentation somehow. I just spent a while trying to figure out why my program ran on Windows 7 but crashed on Windows XP. Switching from runThread() to launchThread() fixed it. I didn't suspect runThread() was frowned upon until I discovered this thread.


#14

TBH modal loops work fine on both Win7 and WinXP, and if switching to launchThread seemed to fix a crash, you should be very suspicious that it's not something else in your code that's broken.

EDIT: BTW I'm not saying you should use modal loops - you definitely shouldn't! But they also shouldn't be blamed for a crash unless you can prove it's not your own code at fault!


#15

That's interesting indeed. Another case is, how to open a file dialog without a modal loop? It looks like at least on Windows using native file dialogs, this is not possible at all.

--
Roeland