Assertion hit when creating Drawable in Worker Thread


#1

I would like to create a component in a worker thread and pass it to the MessageManager to include it in the interface, once it’s constructed.
The Component uses an image and does some drawings with SVG, so this can be onerous for the Message Thread according to the size of the image.

As of Juce 5, by calling juce::Drawable::createFromImageData() from the worker thread, the program hits an assertion (ASSERT_MESSAGE_MANAGER_IS_LOCKED) in juce::Component::internalRepaintUnchecked().

I would like to know:

  1. If it’s bad practice anyway to create a Component in a thread other than the Message Thread, even though it is passed later on to the Message Thread in order to be added to the GUI.
  2. If I can somehow by-pass the assertion in case there is no dangerous side effect in point 1
  3. If there are indeed any side effects, how could I create an “heavy” component without freezing the Message Thread ?

thanks


#2

Yes, unfortunately it is bad practive as calling any component code will have introduce hard to debug and potentially rare race-conditions if the the worker thread creates the component while the message thread is also in component code.

The assertion is there for a reason and you should not by pass it. That’s the thing with race conditions: You may not be experiencing a crash but that doesn’t mean that your code is correct. It may still crash on your customer’s computer :frowning:.

I don’t think you can. You can lock the message thread by taking a message manager lock and then proceed to create your component on the worker thread - but the message thread will be blocked.


#3

Thanks for the clarification


#4

What you can do though, is creating a Component and give it an initialise() method. As long as this initialise only prepares internal data and doesn’t affect the components properties like bounds etc. there is no problem to call that initialise from a worker thread.

And you can add a bool isInitialised flag, to skip paint and resized calls before initialised is finished.

As the last thing in your initialised set the isInitialised = true and call repaint (and eventually resized, depending what you needed to initialise) asynchronously via MessageManager::callAsync().


#5

Mhm…not sure that would work.
I need to read an SVG image in memory, and it looks like the only way to do that with Juce is to create a Drawable.

A Drawable is a (inherits from) Component. And the assertion is hit at the moment of construction.


#6

Ah bummer, you can’t split the construction and loading of the SVG to drawable, I can see that.
Must be a huge SVG though, if loading needs to be in a worker thread…

But maybe loading directly into a Path would be an option, if it is only SVG and no pixel data embedded?

Path Drawable::parseSVGPath (const String & svgPath)