When and why exactly to use MessageManagerLock

From the documentation, I read, “Used to make sure that the calling thread has exclusive access to the message loop” and “it’s not thread-safe to call any of the Component or other UI classes from threads other than the message thread.”

Does this mean that in a class that extends Component, all of the methods of the class need to be called from the message thread, or only the methods that are a part of Component?

I imagine that it would have to be locked only if it responds to or possibly sends events.

If the code in your additions follow the restrictions you quoted, then it will not need to be on the Message Manager thread. As an example, let’s say you have an image that is generated by your class, which will be displayed in the UI. You could generate this image on any thread, but only display it in the Message Manager thread. Generally, I believe you will get an assertion in the debugger if you do call a restricted function from a different thread. So you will know if you made a mistake.

That’s what I was hoping.

Still, if you create an image while the Component's paint method is running, the app may try to render it halfway through writing, causing a crash or anomaly. That can be solved by using a mutex, so there isn’t a need to lock the Message Manager.

From what I understand the message loop is what the MessageManagerLock is locking. This loop is just an infinite while(true) loop that: 1) checks for new events, 2) does any processing it needs to do with those events, and then 3) calls the Component's relevant callback, like resized() or sliderValueChanged(), depending on what it is.

So, I’m guessing what we’re avoiding with MessageManagerLock is the Component trying to be resized or something similar while another thread is altering relevant values (like the width, while the Component is trying to resize). Is this a good interpretation?

Well yes. You will still have to handle multi-threading issues as normal. And if you are doing as you describe, I would only apply a lock after regenerating the new image. ie. generate to a temp image, lock, swap in new image.

And yes, I think your interpretation is accurate. mostly. There are also events that use the MM thread, like juce::Timer, and I think the handleAsync stuff., etc. and more directly, if you want to serialize into the MM thread, you can use MessageManager::callAsync();

1 Like

Just using a mutex is then going to cause one of the threads to spin whilst there is contention, which will negate the point of generating the image off-message-thread presumably because it was taking too long. Better is to double buffer the image, so you have one image that can be displayed by paint() and another that can be drawn to in the other thread and swap them accordingly.

edit: oops, missed the 2nd senetence of cpr2323’s reply, which is basically saying the same thing.

Unless you create background threads you don’t need to lock yourself. The Component base class takes care of it, and most methods are single threaded anyway. All paint() calls are on the message thread, all resized() calls are from the message thread, mouse callbacks and timer callbacks.

It is different if you have a custom OpenGLRenderer that renders on a background thread. In this case it is your job to make sure to be thread safe. Usually by copying the data into the renderer or using a FIFO etc.

Problematic accessing from threads other than the message thread is getX(), gtY(), getWidth() etc. All the informations that are definitely shared with the operating system.