AsyncUpdater which don't malloc

It works fine if I use the default AsyncUpdater so my mods are causing the crash.
If you can spend two minutes taking a look at my code if you spot something weird.

FWIW this code used to work fine in Juce 1.46 and used in numerous product without any issue so it look like to be related
to a changes in Juce, maybe regarding MessageListener or something like that or AsyncUpdater used to behave in very specific cases.

Thanks,

Well, I had a quick look but your code looks fine to me! I’ve really no idea, I guess you need to try removing bits of it to narrow down the cause of the problem. Let me know if you find anything, I’d be interested to know what the problem is!

Ohh that was a nasty issue.

I didn’t rebuild the amalgamated files so juce_amalgamated.h didn’t contains the right code.

The only weird thing is that I’m using the template version not amalgamated (JuceLibraryCode1.cpp, …)
and I’m seeing in the code that it use juce_amalgamated.h line 82

#if JUCE_BUILD_NATIVE
#include “…/juce_amalgamated.h” // FORCE_AMALGAMATOR_INCLUDE
#endif

Is there a reason that it doesn’t the normal include version ?

Thanks,

Nasty!

There’s no particular reason why it uses the amalg headers, excect that it keeps things a bit simpler for me to maintain, and is quicker to compile, especially from a network drive which is how I do all my win32 compiles (it’s much quicker to read one big file via a network than many small ones)

oki doki.

My new version works fine so feel free to use/modify it in order to have a non malocing async update.(at least in the calling thread)

For a fully non malocing one, I would need to use MessageManager::callFunctionOnMessageThread, unfortunately
it doesn’t post the message if we are on the message thread while postMessage always post it, so it means that the AsyncUpdater would not wait a new round before calling handleAsyncUpdate.

If you add a non clever version of callFunctionOnMessageThread or with a flag then we could implement a totally non mallocing AsyncUpdater.
In any case the major issue was the malloc is the calling thread(possible realtime) and this is fixed with my version.

Here is the header

/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-10 by Raw Material Software Ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the GNU General
   Public License (Version 2), as published by the Free Software Foundation.
   A copy of the license is included in the JUCE distribution, or can be found
   online at www.gnu.org/licenses.

   JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

  ------------------------------------------------------------------------------

   To release a closed-source product which uses JUCE, commercial licenses are
   available: visit www.rawmaterialsoftware.com/juce for more information.

  ==============================================================================
*/

#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__
#define __JUCE_ASYNCUPDATER_JUCEHEADER__

//==============================================================================
/**
    Has a callback method that is triggered asynchronously.

    This object allows an asynchronous callback function to be triggered, for
    tasks such as coalescing multiple updates into a single callback later on.

    Basically, one or more calls to the triggerAsyncUpdate() will result in the
    message thread calling handleAsyncUpdate() as soon as it can.
*/
class JUCE_API  AsyncUpdater
{
public:
    //==============================================================================
    /** Creates an AsyncUpdater object. */
    AsyncUpdater() throw();

    /** Destructor.

        If there are any pending callbacks when the object is deleted, these are lost.
    */
    virtual ~AsyncUpdater();

    //==============================================================================
    /** Causes the callback to be triggered at a later time.

        This method returns immediately, having made sure that a callback
        to the handleAsyncUpdate() method will occur as soon as possible.

        If an update callback is already pending but hasn't happened yet, calls
        to this method will be ignored.

        It's thread-safe to call this method from any number of threads without
        needing to worry about locking.
    */
    void triggerAsyncUpdate();

    /** This will stop any pending updates from happening.

        If called after triggerAsyncUpdate() and before the handleAsyncUpdate()
        callback happens, this will cancel the handleAsyncUpdate() callback.
    */
    void cancelPendingUpdate() throw();

    /** If an update has been triggered and is pending, this will invoke it
        synchronously.

        Use this as a kind of "flush" operation - if an update is pending, the
        handleAsyncUpdate() method will be called immediately; if no update is
        pending, then nothing will be done.
    */
    void handleUpdateNowIfNeeded();

    //==============================================================================
    /** Called back to do whatever your class needs to do.

        This method is called by the message thread at the next convenient time
        after the triggerAsyncUpdate() method has been called.
    */
    virtual void handleAsyncUpdate() = 0;


private:

    friend class InternalAsyncUpdaterThread;

    bool asyncMessagePending;
};


#endif   // __JUCE_ASYNCUPDATER_JUCEHEADER__

Thanks. This design’s great for your specific needs, but I don’t think it’d make a good replacement for the current AsyncUpdater - the overhead of having a hidden thread, and a limit on the number of active instances make it much more of a niche class.

Maybe a better design would be to put everything into a manager class that would handle the thread and lists, e.g.

[code]class LockFreeNotificationManager
{
void addListener (int eventIDToListenFor, Listener* callback);
void removeListener etc

void triggerEvent (int eventID);   // lock-free call

};[/code]

Then user code could be in charge of the lifetime of this object, rather than having static objects and threads floating around behind the scenes.

There is no limit in the number of active instances in this impl.
If you refers to the comment
" // there can’t be another instance of the AsyncUpdater in the fifo as asyncMessagePending would be true so post won’t be called"
it just means that the fifo can never contains the same AsyncUpdater because of how AsyncUpdater is implemented and this is true for the older version as well.

I meant that you pre-allocate a number of slots in the fifo, so if you have too many instances of AsyncUpdaters it could overflow and lose data.

There is as mush slots as there is AsyncUpdaters and according to the impl there can’t be more than one slot used per AsyncUpdater so I don’t see how it could overflow except if the vector can’t allocate. (in that case you would have other issue as well)

Are you sure you are checking the latest impl I’ve copy/paste ?

Ok, maybe I misread the code. But if you’re resizing the fifo when new asyncupdaters is created, how do you avoid race conditions if the realtime threads are trying to push something onto the fifo while it’s being resized?

There is a CriticalSection.

Fair enough. But I still think it’s an inappropriate design for something to include in the library - using a static thread is a total showstopper for me. Didn’t you think my suggestion of a self-contained manager made more sense?

I’m not sure I clearly understood what you meant.
Ar you talking about some nice encapsulation so the code is more elegant or
a solution without using a separate thread ?

The thread is pretty safe as it doesn’t start when the app is launched
but rather when the AsyncUpdater register itself so the order of the static initialization is safe.
Eventually we can start/stop each time there no more AsyncUpdater or we add a new one and the thread was stopped.

Or maybe you want a thread per AsyncUpdater ?

I’m open to suggestions.

Thanks,

My suggestion meant that you’d create a manager object (which would use a thread internally), and then multiple clients can use it to trigger events, and multiple listeners can attach to it. There’s be no static objects, as everything would be contained in this manager object.

It would in no way replace the existing AsyncUpdaters, they already work very well and I’m not going to change them. It would be a different, specialised class for the kind of thing you’re doing.

oh I see.
The problem is that AsyncUpdater is used in audio stuff like GenericAudioProcessorEditor and that mallocing in the audio thread under Protools lead to serious issue.
So AsyncUpdater needs this changes if someone try to use Juce Default Audio processing class and editors in the RTAS format.

Still, I understand you are reluctant to change something that it’s working perfectly for 99,99% of the use case of Juce.

In any case I will keep this in my code as we already done everything using AsyncUpdater.
Maybe other would be interested by the changes I’ve sent.

Thanks,

Ah, I didn’t realise that GenericAudioProcessorEditor was done like that - but that could be updated quite simply using a timer, e.g.

[code]class ProcessorParameterPropertyComp : public PropertyComponent,
public AudioProcessorListener,
public Timer
{
public:
ProcessorParameterPropertyComp (const String& name, AudioProcessor& owner_, int index_)
: PropertyComponent (name),
owner (owner_),
index (index_),
paramHasChanged (false),
slider (owner_, index_)
{
startTimer (100);
addAndMakeVisible (&slider);
owner_.addListener (this);
}

~ProcessorParameterPropertyComp()
{
    owner.removeListener (this);
}

void refresh()
{
    slider.setValue (owner.getParameter (index), false);
}

void audioProcessorChanged (AudioProcessor*)  {}

void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float)
{
    if (parameterIndex == index)
        paramHasChanged = true;
}

void timerCallback()
{
    if (paramHasChanged)
    {
        paramHasChanged = false;
        refresh();
        
        startTimer (1000 / 50);
    }
    else
    {
        startTimer (jmin (1000 / 3, getTimerInterval() + 10));
    }
}

//==============================================================================
juce_UseDebuggingNewOperator

private:
//==============================================================================
class ParamSlider : public Slider
{
public:
ParamSlider (AudioProcessor& owner_, const int index_)
: Slider (String::empty),
owner (owner_),
index (index_)
{
setRange (0.0, 1.0, 0.0);
setSliderStyle (Slider::LinearBar);
setTextBoxIsEditable (false);
setScrollWheelEnabled (false);
}

    void valueChanged()
    {
        const float newVal = (float) getValue();

        if (owner.getParameter (index) != newVal)
            owner.setParameter (index, newVal);
    }

    const String getTextFromValue (double /*value*/)
    {
        return owner.getParameterText (index);
    }

    //==============================================================================
    juce_UseDebuggingNewOperator

private:
    AudioProcessor& owner;
    const int index;

    ParamSlider (const ParamSlider&);
    ParamSlider& operator= (const ParamSlider&);
};

AudioProcessor& owner;
const int index;
bool volatile paramHasChanged;
ParamSlider slider;

ProcessorParameterPropertyComp (const ProcessorParameterPropertyComp&);
ProcessorParameterPropertyComp& operator= (const ProcessorParameterPropertyComp&);

};[/code]