Calling Component::repaint() in a process thread


#1

Hi everyone,

Is it safe to call Component::repaint() from a audioProcess thread?

After the Juce document:

So, does it mean that it is a async call and so, the process thread is not compromised by calling this async UI domain method?

Or maybe, is there another orthodox way to doing such kind of things?

Gabriel


Getting components to redraw while the event loop is blocked
#2

Despite the implications of the docs, repaint() is not thread safe. Although it does just mark a region as dirty and any repaints may be coalesced and actually repainted on subsequent message loops depending on the contents of the message queue.

I think the usual two methods for thread safe repainting are to use an AsyncUpdater or a Timer, basically anything that will be called from the message thread.


#3

So the way to decoupling a calling from a Process thread to a UI message loop thread is by an AsyncUpdater class implementation?


#4

Thats probably the simplest way if all you need to do is call repaint(), its how I usually do it. If you have other actions that need to be deferred to the message thread you may need a custom Message and associated callback.


#5

Thank you dave96!

I’m going to try it.

By the way, impressive libraries you have!

Gabriel


#6
vf::MessageThread::getInstance().call (&Component::repaint, myComponent);

#7

What’s that, Vinn?


#8

It’s the thread-safe / efficient way to cause Component::repaint to be called if you’re using “VFLib” (in my signature). Of course, you can call any function this way. Internally it uses an AsyncUpdater.

Note that calling AsyncUpdater::triggerAsyncUpdate from the audio thread is not a good idea, as it will certainly cause dropouts, on Windows at least. This is because PostMessage, which AsyncUpdater uses internally, can block. VFLib calls triggerAsyncUpdate for you on it’s own separately managed thread, using a lock-free / wait-free queue.

What I’m saying is that if you’re using VFLib (and there’s no reason not to, since it’s MIT Licensed), safely triggering a call to repaint is accomplished using just the one line I provided.


#9

All right, understood. I’m going to check that.

Thanks!


#10

Had all sorts of problems when explicitly calling component::repaint() elsewhere, most of the case with no visible effect (fact is, I think we just queue one more repaint request event). I am notably using an ActionListener as App controller (MVC design) and noted that no component updates would get repainted before the actionListenerCallback completes. However, I discovered:
MessageManager::getInstance()->runDispatchLoopUntil(200);
…that works like magic.
I have not observed any detrimental effect so far, but it is still unclear for me what this 0,2 sec run given to the message manager from my ActionListener controller thread could impact? indeed, Jules-in-person recommends never doing that, cf.: runDispatchLoopUntil(1) sometimes takes many seconds to complete
What would be the alternative?


#11

No - never, never do that!

It’s unsafe for all kinds of subtle reasons, and is even impossible on some platforms like Android, where the system doesn’t allow you to run the message loop synchronously because it’s such a terrible, awful, dangerous thing to do!

I don’t understand your explanation of what problems you’re having with repaint, but maybe start a new thread and try to clearly say what’s not working for you. But it sounds to me like you’re just approaching the whole thing with the wrong mindset if you’re expecting to be able to repaint synchronously when an event happens. Everything you could ever need to do can be achieved with async repainting.


#12

You’re right! I approached the issue from the wrong side. Actually, I discovered an existing discussion thread with a same app pattern as mine, and posted into it the solution I developed, cf. Getting components to redraw while the event loop is blocked
As you propose, we may continue the discussion there, and correct me as needed.