GUI operations from multiple threads


#1

I’m trying to find a way to programmatically control creating a juce window and I’m having trouble. Everything works fine when the calls to construct / initialize the window are in the same thread as I run MessageManager::runDispatchLoop() but when I move either the dispatch loop or the calls to construct / initialize the window to another thread I’m running into what I think is deadlock. I’ve added what I think are the appropriate calls to construct MessageManagerLock objects but no dice. When I leave the MessageManagerLock objects out, a bunch of asserts fire, but the window works as I expect.

In the attached files (meant to end up in juce/extras/multithread), SimpleWindow::Initialize in SimpleWindow.cc has one line of code that seems to cause the deadlock. It’s the first const juce::MessageManagerLock mmLock; there. WIth that commented out, lots of asserts fire, but things work. With that code live, I get what I think is deadlock.

I could use a hand diagnosing this. I can think of two ways to get this to happen all in one thread:

  • use runDispatchLoopUntil and make sure the programmatic control is via some non-blocking method (not my case at the moment unfortunately)
  • use MessageManager::callFunctionOnMessageThread. This works for me and is probably sufficient but I’m curious why I need to do it.

Thanks for any help you can offer.

-DB


#2

Better post a changeMessage (or use a AsyncUpdater) on the Thread, and do the GUI Stuff on the changeListenerCallback ( or handleAsyncUpdate) .
All OS-Calls should be done on one thread (on the MessageThread) to be sure that all Juce<->OS interaction (and internal values) are stable at one point of time.


#3

You mentioned running a dispatch loop in another thread - that wouldn’t make any sense at all because there’s already a dispatch loop running on the main thread, to try to run another one would be pointless/dangerous/silly.

You may be hitting a deadlock creating windows because in win32 the CreateWindow function can only be called by the message thread, so if you try to create a window on a background thread, it’ll internally use callFunctionOnMessageThread. For that to work, the dispatch loop must be running, so if you block it, it’ll deadlock. (You should have hit an assertion if this is what happened though)

Best advice is don’t do any component creation/deletion on a thread, stick to event-based programming for that. You’re ok to do simple component moving, repainting, etc if you use a MM lock, but keep it to a minimum.


#4

[quote]Best advice is don’t do any component creation/deletion on a thread, stick to event-based programming for that.[/quote]Sorry to be dense, but can you point me to some example code that does something like this?
Thanks.


#5

The threads example in the juce demo uses threads to move the components around.


#6

I think things are slowly sinking in about how to make things happen in the GUI thread as expected. The ThreadingDemo class was interesting, but more interesting is the ApplicationCommandTarget that ContentComp inherits from in MainDemoWindow.cpp. I had the brilliant idea to make my own subclass of ApplicationCommandTarget and implement my command(s) there. Then in the run method of my Thread subclass, I can call invoke and things happen as they should…or so it seems at least.

I’ve run into a snag though and I’m curious what other folks would do in this case. My commands aren’t structured quite the same was as juce commands. Everything required to execute the appropriate command seems to be implied from one integer command ID. My commands have arguments, similar to argc/argv on the command line. I had the genius idea to create a subclass of ApplicationCommandTarget::InvocationInfo to store what I need, and then call invoke on it. Then, in my implementation of perform I’d dynamically cast the argument to my subclass and away I’d go.

The snags I’ve run into are:

  • dynamic_cast doesn’t work on ApplicationCommandTarget::InvocationInfo because it declares no virtual functions
  • typeid might work, but my C++ knowledge is too weak to know for sure

Even if it did, ApplicationCommandTarget::tryToInvoke constructs a new ApplicationCommandTarget::InvocationInfo object so even if I pass a subclass, that gets forgotten. It makes sense that ApplicationCommandTarget::tryToInvoke makes a copy of its InvocationInfo argument because it could easily go out of scope before ApplicationCommandTarget::CommandTargetMessageInvoker::handleMessage gets called to use it.

I’m probably abusing the command system by doing this, but if InvocationInfo had a virtual ApplicationCommandTarget::InvocationInfo *Duplicate() method that ApplicationCommandTarget::tryToInvoke uses like this messageInvoker->postMessage (new Message (0, 0, 0, info.Duplicate())); instead of what’s currently there: messageInvoker->postMessage (new Message (0, 0, 0, new ApplicationCommandTarget::InvocationInfo (info))); I think I’d be set…penalizing all current users of InvocationInfo with the virtual function table it doesn’t currently have.

Thoughts? I’m sure there’s a better way. I have a patch to attach but the forum isn’t letting me. I’ll send it in private mail.

Thanks again for your help.

-DB


#7

Hmm, I think you’re pushing it in a direction it’s not really designed to go there… The whole reason for keeping the commands simple enough to be referenced by a single ID number is so that the commands can be attached to menus, buttons, key shortcuts, etc., which can automatically use the manager to deliver them to the most appropriate target.

If you start adding custom commands, the only way to invoke them is to directly create your command object, and then call invoke() on a target… It seems like in those cases it’d be easier to not use the command manager at all, but to just call a method on the target directly.


#8

[quote]It seems like in those cases it’d be easier to not use the command manager at all, but to just call a method on the target directly.[/quote]I’m not sure what you mean by the command manager, but I’m not sure I’m using one. I’m definitely not using an ApplicationCommandManager object. The benefit I get from using a subclass of ApplicationCommandTarget is its CommandTargetMessageInvoker. It might be easier to inherit from MessageListener directly though. I (feel like) I need something to get the actual work done on the GUI thread. I’ll try that.


#9

Inheriting from MessageListener works great.

Thanks for the help.