(executive summary: Jules, at the end I suggest a bug fix that may apply to the Cocoa tree)
Maybe I am not the only one who is shipping a plugin based on the good old carbon Juce, so I am starting this thread to share some fixes on the Carbon code.
Here are the issues that I have run into:
- the blank window bug : this is fixed by http://www.rawmaterialsoftware.com/juceforum/viewtopic.php?p=17011#17011
- window repaint problems in protools: commenting the
SetControlSupervisor (pluginView, parentView); startTimer (150);
seems to work. - mouse offset bug in protools : still occuring , but moving the mouse cursor over the lower part of the plugin window make it disappear…
And finally the bug that is really annoying me:
- Garageband, Logic, Protools all showed some strange event-loop related bugs that render the host GUI very unresponsive with a lag that can reach 10 or 20 seconds between a mouse click and the UI reaction to the click. The shark profiler shows that it spends much cpu time processing messages within very deep hierarchy of windows. The bug happens even when the plugin GUI has not been opened at all.
The most easy host to spot that bug being Garageband 5, because the bug is almost always reproducible on this one. Just take an empty audiounit juce plugin, remove everything from juce_AudioUnitWrapper.cpp (AUView included), except a timer with small period (lets says 20ms), open it in Garageband and everything in Garageband become really slow. Each click in the interface makes it slower. The same stuff happens in Logic, but more randomly (for example after two bounces of your project), and Pro Tools (but not everywhere, not everytime, not on my setup…)
I have never done Carbon dev, so I’m not exactly knowledgable on carbon event loops. Here is what I have observed: when the UI lag happens, the host event queue seems to be stuck with some events that won’t go. Each mouse click in the interface adds new events in the queue, and those events are not processed by the host. Juce is not exactly flooding the event loop of the host, but the juce Timers are causing a constant flux of event being sent to the host. Those events are sent with a “Standard” priority:
Using kEventPriorityHigh instead of Standard has the immediate effect of just locking Garageband, it looks like it is not able to process any event when the Juce evt is the first one in the queue ?
Using kEventPriorityLow makes everything work more smoothly. This is almost enough to remove the strange UI lag, but on some occasions I see the event queue grow very quickly with stuck events.
When this kind of situation happens, the juce InternalTimerThread is not helping because it keeps filling the event queue even when the previous timer event sent has not yet been received: it is using a boolean flag “callbackNeeded” that tells it that “an” event has just been received, but it is not necessariy the last event sent. It can be an event that was sent 10 seconds ago… so this flag is not efficient and should be replaced by two counters send_cnt and recv_cnt. Here is what I am now using in the run() method of the InternalTimerThread:
if (timeUntilFirstTimer <= 0)
{
++send_cnt;
postMessage (new Message(send_cnt, 0, 0, 0));
const uint32 messageDeliveryTimeout = now + 2000;
while (recv_cnt != send_cnt)
{
wait (4);
That is I have replaced the “callbackNeeded” bool flag with two integers. In the handleMessage, I replaced the callbackNeeded = false;
with recv_cnt = msg.intParameter1;
With that fix, the timer thread is no more flooding the host event queue when it does not want to read events fast enough.
Quickly looking at the current (Cocoa) juce_Timer.cpp file, I believe a similar correction should be made to it. Jules, what do you think ?
And finally, a last question that may be plain stupid: what if I was just calling timerCallback from the InternalTimerThread::run() method , using a MessageManagerLock to make it thread-safe ? Would there still be some thread problems ? to me it looks like it would make the timer callbacks much more precise and less dependant on the host message loop.