AsyncUpdater in ComboBox failing to update


#1

For some reason the AsyncUpdater class fails to deliver messages:

void AsyncUpdater::triggerAsyncUpdate()
{
    if (activeMessage->shouldDeliver.compareAndSetBool (1, 0))
        activeMessage->post();
}

the post() method never executes for some combo boxoes for some reason (however if i create new combos they execute).

Is there something in JUCE that might cause the AsyncUpdater to selectively stop working on some combos ?

This is windows 7, 64bits, VisualStudio 2010 (sp1), i'm debugging a 32bit Debug build with a recent JUCE commit (2 weeks old at the most).

I'd be happy to provide more info i just don't know what to debug, there are a lot of Atomic 32bit specific casts happening, and causing compareAndSetBool to return false.


#2

There's no reason why it would mysteriously fail to trigger - that'd only happen if you delete or cancel the asyncupdater. Get a breakpoint on the destructor and you might catch it, I bet it's just something deleting your object.


#3

But this AsyncUpdater is the base of the ComboBox class, and the Combo never gets deleted (it's always on the screen) the triggerAsyncUpdate() method is in the change method of the ComboBox class:

void ComboBox::sendChange (const NotificationType notification)
{
    if (notification != dontSendNotification)
        triggerAsyncUpdate();

    if (notification == sendNotificationSync)
        handleUpdateNowIfNeeded();
}

this should cause the handleAsyncUpdate to trigger, it never does


#4

I don't think there's a mistake there - the AsyncUpdater has been hammered over the years and it's highly unlikely that there's anything wrong with it. Sounds to me like you're just misunderstanding something, but I'm not sure what!


#5

Yeah i'm sure i'm missing something obvious i just don't know what.

The problem is i have a combo and during it's lifetime for some unknown reason it stops notifying the defined listener of a change. It works the menu shows you can select it etc. but i never get the callback i should be getting.


#6

The only possible way I can imagine that happening would be the update message was dispatched, but the OS failed to deliver it. On older versions of Windows that could sometimes happen if your app massively flooded the message queue, but I wouldn't expect it to be likely on modern versions, unless maybe you're blocking the message queue for extended periods of time whilst also flooding it with messages?


#7

Well i have to say weird thing might happen i'm using Lua and most of JUCE is bound to it using luabind. Although i have not exposed any AsyncUpdater methods or that class directly. Though i have exposed the ComboBox api and there is a lot of objects that get created very quickly on application load (about 3000) and all of them have some sort of Lua code, so the message queue might fill up.

But like i wrote, although previous combo boxes fail to trigger change events, new ones (created dynamicly) work fine.

The problem is i can't debug this on my level because of that ATOMIC stuff, if that doesn't work then the whole engine stops working. I'm getting not assertions or anything from the debugger, no memory corruptions no runtime errors.


#8

But like i wrote, although previous combo boxes fail to trigger change events, new ones (created dynamicly) work fine.

Well yes, that's what you'd expect, because if a box has sent a message and is stuck waiting for it to arrive, then it won't work. But that won't affect new ones, which will be fine until they also lose a message.


#9

Any ideas on how to debug this issue, is there some way to flush the message queue ?

Can this be the cause why the Atomic methods are failing ?

I changed the method sendChange of the ComboBox to do this:

void ComboBox::sendChange (const NotificationType notification)
{
    //if (notification != dontSendNotification)
        //triggerAsyncUpdate();

    //if (notification == sendNotificationSync)
        handleUpdateNowIfNeeded();
}

but the Atomic::exchange method seems to be returning 0 always, so the handleAsyncUpdate() never gets executed.


#10

No.. you're getting confused. Atomic ops will never fail, but if the message get sent to the queue but doesn't arrive, the updater will not send another message. The whole purpose of that atomic flag is to prevent it sending another message while one is still "out there". But if the OS eats it, the class has no way to know that it'll never arrive, so the flag will never get reset.

And no, flushing the queue wouldn't help even if it was possible. If what I suggested is true, then the message has just been discarded when the queue was full.


#11

I'll see if the same happens on other OSes (Linux, OSX).


#12

So i just tested this on Linux, and it works fine, so it looks like a Windows specific bug.

Is there anyone here that could direct me to some reading on how to approach this, is there some artifical way to avoid this (delay events when i know there will be a lot of them occuring?). I don't know if it's the amount of messages or the amount in a small period of time ?


#13

What are you doing that creates 3000 combo boxes in one go? Are they all visible at once? Could you not create them on demand when required if some are offscreen?


#14

It's not 3000 combos, it's a lot of different components, they are created based on an XML document that a user can create (he/she can add multiple components to one document, in tabs/groups/layers etc)


#15

What i did will be considered bad and a big no-no. But i did it anyway, and for now it works

if (MessageManager::getInstance() && ++dispatchCounter > 300)
+ {
+ _DBG("CtrlrPanelCanvas::restoreState more then 300 modulators created, dispatch pending messages");
+ MessageManager::getInstance()->runDispatchLoopUntil (25);
+ dispatchCounter = 0;
+ }

So in a loop where my objects are created from ValueTree childrem, once every 300 creating i let the MessageManager to deliver pending messages for a shoty amount of time. It does the trick for now.


#16

Wow, good hack! But like you say, it's unfortunately not something I could add to the library!

..just one quick idea: Have a look inside the MessageManager::MessageBase::post() method. In there, it checks whether the system queue post function failed, and if so, deletes the message object. I don't know whether in your case win32 is indeed correctly reporting a failure, but if it is, then I could modify MessageManager::MessageBase::post() to return a bool to pass on the fact that it failed. Then, AsyncUpdater::triggerAsyncUpdate() could react to this by not setting its flag. That'd mean that the event would fail to trigger once, but would work correctly when re-triggered later.


#17

I wouldn't event ask to add that anywhere, this is a very app specific thing. I'll try your suggestion later today.


#18

As always you were right JULES, i added a piece of code to the post() method

const bool postRet = postMessageToSystemQueue (this);
if (postRet == false)
{
   Logger::writeToLog ("MessageManager::MessageBase::post postMessageToSystemQueue returned false");
}

and it in fact returns false when the message queue gets overloaded. You wrote that this is the case for older versions of windows, and this is Windows 7 SP1 and this happens, is there a known fix for this ?


#19

You wrote that this is the case for older versions of windows, and this is Windows 7 SP1 and this happens, is there a known fix for this ?

No, there's no way to stop the OS dropping the message if it chooses to. Did you try my suggestion of a workaround?


#20

I don't know if I understand the logic here, but i did:

void AsyncUpdater::triggerAsyncUpdate()
{
    if (activeMessage->post())
        activeMessage->shouldDeliver.compareAndSetBool (1, 0);
}

instead of

void AsyncUpdater::triggerAsyncUpdate()
{
    if (activeMessage->shouldDeliver.compareAndSetBool (1, 0))
        activeMessage->post();
}

And assumimg the fix where post() returns false when windows fails, this seems to fix the issue.

But this logic does not to me :)